A.Binary lmbalance(思维)
题意:
给出一个01
字符串,你可以对字符串进行无限次下列操作:
-
选择一个位置 i ( 1 ≤ i ≤ ∣ s ∣ − 1 , |s|为字符串s的长度 ) i(1 \le i \le |s| - 1,\text{|s|为字符串s的长度}) i(1≤i≤∣s∣−1,|s|为字符串s的长度)
-
如果 s [ i ] ≠ s [ i + 1 ] s[i] \ne s[i + 1] s[i]=s[i+1],在 s [ i ] s[i] s[i]和 s [ i + 1 ] s[i + 1] s[i+1]之间插入一个
'0'
-
如果 s [ i ] = s [ i + 1 ] s[i] = s[i + 1] s[i]=s[i+1],在 s [ i ] s[i] s[i]和 s [ i + 1 ] s[i + 1] s[i+1]之间插入一个
'1'
-
问:能否在经过一些操作后,使得字符串中0
的数量严格大于1
的数量。
分析:
只要字符串中同时存在01
,那么就可以无限产生0
,此时必然成立,如果字符串中没有1
,那么也必然成立。
只会当字符串中只有1
时,才无法使0
的数量严格大于1
的数量。
代码:
#include <bits/stdc++.h>typedef long long LL;
using namespace std;
const int N = 3e5 + 5;void solve() {int n;string s;cin >> n >> s;int one = 0, zero = 0;for (int i = 0; i < n; i++) {if (s[i] == '0') zero++;else one++;}if (one && !zero) {//只有1出现,0没有出现才是NOcout << "NO" << endl;} else {cout << "YES" << endl;}
}int main() {int Case;cin >> Case;while (Case--) {solve();}return 0;
}
B.Getting Points(数学)
题意:
有 n n n天时间可以学习(第一天为周一),每天可以去上课,上课将获得 l l l点学分,每周一会布置一个课后练习,完成练习可以获得 t t t点学分。
每天可以选择休息或去上课,如果选择休息,那么无法得到当天的学习学分,且不能完成课后练习。如果选择去上课,可以获得上课的学分,且每天去上课可以在未完成的练习中选择不超过两个练习任务完成。
问:需要获得 P P P点学分才能毕业,最多可以休息多少天。
分析:
分两种情况进行考虑:
-
1.如果每次去上课均完成两个任务,且这样就能获取足够的学分,那么就计算最少需要去上课几天。
-
2.如果仅用最少上课天数去完成任务无法达到毕业学分,那么就需要额外的时间去上课,计算所需的额外天数。
题意:
#include <bits/stdc++.h>typedef long long LL;
using namespace std;
const int N = 3e5 + 5;void solve() {LL n, l, t, P;cin >> n >> P >> l >> t;LL task_cnt = n / 7 + (n % 7 != 0);//计算练习任务的数量LL lesson_cnt = (task_cnt + 1) / 2;//计算完成练习任务所需的天数LL earn = task_cnt * t + lesson_cnt * l;//每次去上课都可以获得上课的学分以及任务的学分if (earn >= P) {//此时已经拿够学分了LL one_earn = t * 2 + l;//计算一天可以获得的学分LL ans = P / one_earn;//计算所需的天数if (ans * one_earn < P) {ans++;}cout << n - ans << endl;} else {P -= earn;//计算还需要获得的学分LL ans = lesson_cnt + P / l + (P % l != 0);//此时每天只能获得上课的学分,计算额外的天数cout << n - ans << endl;}
}int main() {int Case;cin >> Case;while (Case--) {solve();}return 0;
}
C.Insert and Equalize(GCD)
题意:
给出一个包含 n n n个数字的数组 a a a,其中每个数字均不相同。
你需要在数组 a a a中插入一个数组 a a a中没有的数字,然后选择一个 x x x,对数组中每个数通过加上若干次 x x x后,使得所有数字均相同。
问:最少需要多少次操作,才能使得数组中所有数字均相同?
分析:
对 a a a数组进行排序(此时数据存储于下标 1 ∼ n 1 \sim n 1∼n)
令 a [ n ] a[n] a[n]为操作完最终的数,为了使所有数字均能通过加上 x x x变为 a [ n ] a[n] a[n],且操作次数最少,那么需要对 a a a数组所有数变为 a [ n ] a[n] a[n]所需加上的值取GCD
(最大公约数)。
然后考虑插入的新数字怎么选择,考虑如果选择最大的数字加上 x x x的值做为新的数字,那么此时的操作次数需要加上 n n n(原数组中 n n n个数字还需再加上)。
而如果能选择一个满足 a [ n ] − k × x ( k < n ) a[n] - k \times x(k \lt n) a[n]−k×x(k<n)的一个数字,那么只需要额外 k k k次操作就能使新插入的数字也变为 a [ n ] a[n] a[n],此时操作次数一定小于前面的方案,此时的 k k k可以通过for
循环进行枚举获得。
代码:
#include <bits/stdc++.h>typedef long long LL;
using namespace std;
const int N = 3e5 + 5;int n;
LL a[N];void solve() {cin >> n;for (int i = 1; i <= n; i++) {cin >> a[i];}sort (a + 1, a + n + 1);LL x = a[n] - a[1];for (int i = 2; i <= n; i++) {x = __gcd(x, a[n] - a[i]);}x = max(1ll, x);//考虑n为1时的情况LL cnt = 0;for (int i = 1; i <= n; i++) {cnt += (a[n] + x - a[i]) / x;}LL ans = cnt;for (int i = n - 1, k = 1; i >= 1; i--, k++) {if (a[i] != a[n] - k * x) {//找最小的kans = min(ans, cnt - n + k);break;}}cout << ans << endl;
}int main() {int Case;cin >> Case;while (Case--) {solve();}return 0;
}
D. Robot Queries(思维+STL)
题意:
有一个无限的二维网格。一开始,机器人站在 ( 0 , 0 ) (0,0) (0,0)点。机器人可以执行四条指令:
U , D , L , R U,D,L,R U,D,L,R分别表示向上下左右走一步。现在给你一个由 U D L R UDLR UDLR构成的操作序列 s s s,以及 q q q次查询,每次查询给出 x , y , l , r x,y,l,r x,y,l,r,表示将 s [ l ] − s [ r ] s[l]-s[r] s[l]−s[r]这段操作序列翻转。
询问机器人在执行命令序列 s s s时是否访问了点 ( x , y ) (x,y) (x,y)
分析:
序列不翻转的情况下,走到每一个字母对应的位置一定是固定的,这样的情况查询每个点有没有被经过只要记录一下即可,如果进行了区间翻转, 1 1 1 到 l − 1 l-1 l−1 以及 r r r 到 n n n 的位置仍然是不变的,只有 l l l 到 r r r 之间进行了中心翻转,通过画图可以发现如果 ( x , y ) (x,y) (x,y)翻转之后的坐标为 ( s x [ l − 1 ] + s x [ r ] − x , s y [ l − 1 ] + s y [ r ] − y ) (sx[l-1]+sx[r]-x,sy[l-1]+sy[r]-y) (sx[l−1]+sx[r]−x,sy[l−1]+sy[r]−y),只要查询这个点所记录的所有达到这个点的位置是否在 ( l , r ) (l,r) (l,r)之间即可
代码:
#include <bits/stdc++.h>using namespace std;
typedef long long LL;
int sx[200005], sy[200005];int main() {int n, q;cin >> n >> q;string s;cin >> s;map<pair<int, int>, vector<int>> mp;int x = 0, y = 0;sx[0] = 0, sy[0] = 0;mp[{x, y}].push_back(0);for (int i = 0; i < n; i++) {if (s[i] == 'U') {y++;}if (s[i] == 'D') {y--;}if (s[i] == 'R') {x++;}if (s[i] == 'L') {x--;}mp[{x, y}].push_back(i + 1);sx[i + 1] = x, sy[i + 1] = y;}while (q--) {int x, y, l, r;cin >> x >> y >> l >> r;int ok = 0;if (mp[{x, y}].size() >= 1) {if (mp[{x, y}][0] < l || mp[{x, y}][mp[{x, y}].size() - 1] >= r) {ok = 1;cout << "YES" << endl;continue;}}int xx = sx[l - 1] + sx[r] - x;int yy = sy[l - 1] + sy[r] - y;if (mp[{xx, yy}].size() >= 1) {auto k = lower_bound(mp[{xx, yy}].begin(), mp[{xx, yy}].end(), l);if (k != mp[{xx, yy}].end() && *k < r)ok = 1;}if (ok == 1) {cout << "YES" << endl;} elsecout << "NO" << endl;}return 0;
}
E. Collapsing Strings (字典树)
题意:
给 n n n个字符串 s [ i ] s[i] s[i], ∣ s [ i ] ∣ \vert s[i]\vert ∣s[i]∣表示 s [ i ] s[i] s[i]的长度,两个字符串的合并 C ( a , b ) C(a,b) C(a,b)的运算如下:
- 如果 a a a为空,则 C ( a , b ) = b C(a,b)=b C(a,b)=b
- 如果 b b b为空,则 C ( a , b ) = a C(a,b)=a C(a,b)=a
- 如果 a a a的最后一个字母等于 b b b 的第一个字母,则 C ( a , b ) = C ( a 1 , ∣ a ∣ − 1 , b 2 , ∣ b ∣ ) C(a,b)=C(a_1,|a|-1,b_2,|b|) C(a,b)=C(a1,∣a∣−1,b2,∣b∣) ,其中 s l , r s_{l,r} sl,r 是 s s s 从 l l l 字母到 r r r 字母的子串;
- 否则为 C ( a , b ) = a + b C(a,b)=a+b C(a,b)=a+b,即两个字符串的连接。
询问 ∑ i = 1 n ∑ j = 1 n ∣ C ( s i , s j ) ∣ \sum_{i=1}^{n}\sum_{j=1}^{n} \vert C(s_i,s_j) \vert ∑i=1n∑j=1n∣C(si,sj)∣
分析:
每一个字符串都要和其他包括自己的所有字符进行 C C C操作,对于任何一个固定的字符串 s s s,它要和所有的字符串进行拼接,也就是要减去最长相等前后缀的贡献。用字典树存所有的前缀的个数。枚举所有字符串后缀从长往短搜,每次先加上当前字符串总长,再考虑容斥从答案中减去重复的答案。
记录前一个后缀匹配前缀的个数 l s t lst lst,当前后缀匹配前缀的个数 c u r cur cur,以当前后缀作为匹配后缀减去的个数为 c u r − l s t cur-lst cur−lst,答案减去这一部分即可。
代码:
#include <bits/stdc++.h>using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int ch[N][26], cnt[N], idx;void insert(string s) {int p = 0;for (int i = 0; i < s.size(); i++) {int j = s[i] - 'a'; // 字母映射if (!ch[p][j])ch[p][j] = ++idx;p = ch[p][j];cnt[p]++;}
}vector<int> query(string s) {vector<int> v(s.size(), 0);int p = 0;for (int i = 0; i < s.size(); i++) {int j = s[i] - 'a';if (!ch[p][j])break;p = ch[p][j];v[i] = cnt[p];}return v;
}int main() {int n;cin >> n;vector<string> s(n);int tot = 0;for (int i = 0; i < n; i++) {cin >> s[i];insert(s[i]);tot += s[i].size();}LL ans = 0;for (int i = 0; i < n; i++) {reverse(s[i].begin(), s[i].end());int len = s[i].size();ans += tot + 1LL * len * n;int lst = 0;auto v = query(s[i]);for (int j = s[i].size() - 1; j >= 0; j--) {int cur = v[j];int ad = cur - lst;lst = cur;ans -= 2 * (j + 1) * ad;}}cout << ans << endl;return 0;
}
学习交流
以下为学习交流QQ群,群号: 546235402,每周题解完成后都会转发到群中,大家可以加群一起交流做题思路,分享做题技巧,欢迎大家的加入。