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没有出现才是NO
cout << "NO" << endl;
} else {
cout << "YES" << endl;
}
}
int main() {
int Case;
cin >> Case;
while (Case--) {
solve();
}
return 0;
}
B.Getting Points(数学)
题意:
有nnn天时间可以学习(第一天为周一),每天可以去上课,上课将获得lll点学分,每周一会布置一个课后练习,完成练习可以获得ttt点学分。
每天可以选择休息或去上课,如果选择休息,那么无法得到当天的学习学分,且不能完成课后练习。如果选择去上课,可以获得上课的学分,且每天去上课可以在未完成的练习中选择不超过两个练习任务完成。
问:需要获得PPP点学分才能毕业,最多可以休息多少天。
分析:
分两种情况进行考虑:
-
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)
题意:
给出一个包含nnn个数字的数组aaa,其中每个数字均不相同。
你需要在数组aaa中插入一个数组aaa中没有的数字,然后选择一个xxx,对数组中每个数通过加上若干次xxx后,使得所有数字均相同。
问:最少需要多少次操作,才能使得数组中所有数字均相同?
分析:
对aaa数组进行排序(此时数据存储于下标1∼n1 \sim n1∼n)
令a[n]a[n]a[n]为操作完最终的数,为了使所有数字均能通过加上xxx变为a[n]a[n]a[n],且操作次数最少,那么需要对aaa数组所有数变为a[n]a[n]a[n]所需加上的值取GCD(最大公约数)。
然后考虑插入的新数字怎么选择,考虑如果选择最大的数字加上xxx的值做为新的数字,那么此时的操作次数需要加上nnn(原数组中nnn个数字还需再加上)。
而如果能选择一个满足a[n]−k×x(k<n)a[n] - k \times x(k \lt n)a[n]−k×x(k<n)的一个数字,那么只需要额外kkk次操作就能使新插入的数字也变为a[n]a[n]a[n],此时操作次数一定小于前面的方案,此时的kkk可以通过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) {//找最小的k
ans = 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,RU,D,L,RU,D,L,R分别表示向上下左右走一步。现在给你一个由UDLRUDLRUDLR构成的操作序列sss,以及qqq次查询,每次查询给出x,y,l,rx,y,l,rx,y,l,r,表示将s[l]−s[r]s[l]-s[r]s[l]−s[r]这段操作序列翻转。
询问机器人在执行命令序列sss时是否访问了点 (x,y)(x,y)(x,y)
分析:
序列不翻转的情况下,走到每一个字母对应的位置一定是固定的,这样的情况查询每个点有没有被经过只要记录一下即可,如果进行了区间翻转,111 到 l−1l-1l−1 以及 rrr 到 nnn 的位置仍然是不变的,只有 lll 到 rrr 之间进行了中心翻转,通过画图可以发现如果(x,y)(x,y)(x,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)(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;
} else
cout << "NO" << endl;
}
return 0;
}
E. Collapsing Strings (字典树)
题意:
给nnn个字符串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)的运算如下:
- 如果 aaa为空,则 C(a,b)=bC(a,b)=bC(a,b)=b
- 如果 bbb为空,则 C(a,b)=aC(a,b)=aC(a,b)=a
- 如果aaa的最后一个字母等于 bbb 的第一个字母,则 C(a,b)=C(a1,∣a∣−1,b2,∣b∣)C(a,b)=C(a_1,|a|-1,b_2,|b|)C(a,b)=C(a1,∣a∣−1,b2,∣b∣) ,其中 sl,rs_{l,r}sl,r 是 sss 从 lll 字母到 rrr 字母的子串;
- 否则为 C(a,b)=a+bC(a,b)=a+bC(a,b)=a+b,即两个字符串的连接。
询问∑i=1n∑j=1n∣C(si,sj)∣\sum_{i=1}^{n}\sum_{j=1}^{n} \vert C(s_i,s_j) \vert∑i=1n∑j=1n∣C(si,sj)∣
分析:
每一个字符串都要和其他包括自己的所有字符进行CCC操作,对于任何一个固定的字符串sss,它要和所有的字符串进行拼接,也就是要减去最长相等前后缀的贡献。用字典树存所有的前缀的个数。枚举所有字符串后缀从长往短搜,每次先加上当前字符串总长,再考虑容斥从答案中减去重复的答案。
记录前一个后缀匹配前缀的个数lstlstlst,当前后缀匹配前缀的个数curcurcur,以当前后缀作为匹配后缀减去的个数为 cur−lstcur-lstcur−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,每周题解完成后都会转发到群中,大家可以加群一起交流做题思路,分享做题技巧,欢迎大家的加入。

1277

被折叠的 条评论
为什么被折叠?



