给出一个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
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;
}
有 n n n天时间可以学习(第一天为周一),每天可以去上课,上课将获得 l l l点学分,每周一会布置一个课后练习,完成练习可以获得 t t t点学分。
每天可以选择休息或去上课,如果选择休息,那么无法得到当天的学习学分,且不能完成课后练习。如果选择去上课,可以获得上课的学分,且每天去上课可以在未完成的练习中选择不超过两个练习任务完成。
问:需要获得 P P P点学分才能毕业,最多可以休息多少天。
分两种情况进行考虑:
1.如果每次去上课均完成两个任务,且这样就能获取足够的学分,那么就计算最少需要去上课几天。
2.如果仅用最少上课天数去完成任务无法达到毕业学分,那么就需要额外的时间去上课,计算所需的额外天数。
#include
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;
}
给出一个包含 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
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;
}
有一个无限的二维网格。一开始,机器人站在 ( 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
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;
}
给 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)的运算如下:
询问 ∑ 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
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,每周题解完成后都会转发到群中,大家可以加群一起交流做题思路,分享做题技巧,欢迎大家的加入。