Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
Split into two halves and . The form is not about four independent blocks; it says is a cyclic shift of .
For a fixed shift, each position of is matched with one position of . If the two constraints force different letters, that shift is impossible; if both are ?, that matched pair contributes a factor of .
Summing over all shifts overcounts. If has cyclic period , then the same completed string is valid for multiple shifts: exactly one residue class modulo .
Count by allowed period . Compress all positions with the same residue modulo in each half. A string with minimal period is counted times.
For every divisor , compute all shift scores using circular correlations: mismatch counts for forced a vs b, and free counts for ? vs ?. NTT handles all shifts at once; then invert the period overcount.
Let the input length be . Write the first half of as and the second half as .
The condition
means
So is a cyclic shift of . Conversely, if is any cyclic shift of , choosing as the prefix moved to the end gives an decomposition. That is the first big simplification.
Now the obvious plan is: try every shift and add the number of completions. That is wrong, because periodic strings get counted multiple times. This is the little landmine in the problem.
Suppose has minimal cyclic period , where . If one shift works, then every shift congruent to it modulo also works. Among all shifts, the same completed string appears times.
We fix that by counting through periods.
For every divisor of , define as follows: force both halves to have period dividing , then sum the number of valid completions over the possible shifts modulo .
Let be the number of valid completed strings whose first half has minimal cyclic period exactly . Then every such string contributes to iff , and in that case it contributes times. Therefore
We compute for all divisors , then recover in increasing order:
The answer is
Now we need to compute one fast.
For a fixed , compress residues modulo . For each residue , collect all constraints from the first half positions into , and similarly collect constraints from the second half into .
Each is one of:
?;a;b;a and b appear in the same residue class.If any residue is impossible, then .
For a shift , residue in the first half is paired with residue in the second half. The shift is valid iff there is no forced a matched with forced b. If it is valid, each pair where both sides are unknown contributes two choices, and every other pair contributes one choice. So the contribution is
where is the number of pairs (unknown, unknown) under shift .
So we need, for every circular shift :
and
These are circular cross-correlations. To compute one correlation , reverse , duplicate , convolve them with NTT, and read coefficient . We do this three times: a vs b, b vs a, and ? vs ?.
Then
Edge cases are naturally covered:
Complexity per test case is
with a small constant number of NTTs per divisor. The total input size is bounded, so this fits comfortably.
Research note: I checked the official Codeforces tutorial page, the accepted-submissions status page, and an independent Luogu write-up. The official 2201E section was still a placeholder, so the useful external evidence was the accepted submissions plus the cyclic-shift/minimal-period/NTT outline; the statement above is the source of truth.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
const int MOD = 998244353;
const int ROOT = 3;
int modPow(ll a, ll e) {
ll r = 1;
while (e > 0) {
if (e & 1) r = r * a % MOD;
a = a * a % MOD;
e >>= 1;
}
return (int)r;
}
void ntt(vector<int>& a, bool invert) {
int n = (int)a.size();
for (int i = 1, j = 0; i < n; i++) {
int bit = n >> 1;
for (; j & bit; bit >>= 1) j ^= bit;
j ^= bit;
if (i < j) swap(a[i], a[j]);
}
for (int len = 2; len <= n; len <<= 1) {
int wlen = modPow(ROOT, (MOD - 1) / len);
if (invert) wlen = modPow(wlen, MOD - 2);
for (int i = 0; i < n; i += len) {
ll w = 1;
int half = len >> 1;
for (int j = 0; j < half; j++) {
int u = a[i + j];
int v = (int)(a[i + j + half] * w % MOD);
int x = u + v;
if (x >= MOD) x -= MOD;
int y = u - v;
if (y < 0) y += MOD;
a[i + j] = x;
a[i + j + half] = y;
w = w * wlen % MOD;
}
}
}
if (invert) {
int invN = modPow(n, MOD - 2);
for (int& x : a) x = (int)((ll)x * invN % MOD);
}
}
vector<int> pw2;
string T;
int halfLen;
int codeChar(char c) {
if (c == '?') return 0;
if (c == 'a') return 1;
return 2;
}
void addCorrelation(const vector<int>& x, const vector<int>& y, int vx, int vy, vector<int>& dest) {
int d = (int)x.size();
int sz = 1;
while (sz < 3 * d) sz <<= 1;
vector<int> a(sz), b(sz);
for (int i = 0; i < d; i++) a[d - 1 - i] = (x[i] == vx);
for (int i = 0; i < 2 * d; i++) b[i] = (y[i % d] == vy);
ntt(a, false);
ntt(b, false);
for (int i = 0; i < sz; i++) a[i] = (int)((ll)a[i] * b[i] % MOD);
ntt(a, true);
for (int s = 0; s < d; s++) dest[s] += a[d - 1 + s];
}
int calcPeriod(int d) {
vector<int> x(d), y(d);
for (int i = 0; i < halfLen; i++) {
x[i % d] |= codeChar(T[i]);
y[i % d] |= codeChar(T[halfLen + i]);
}
for (int i = 0; i < d; i++) {
if (x[i] == 3 || y[i] == 3) return 0;
}
const int DIRECT_LIMIT = 80;
if (d <= DIRECT_LIMIT) {
int res = 0;
for (int s = 0; s < d; s++) {
int bad = 0, freeCnt = 0;
for (int i = 0; i < d; i++) {
int a = x[i], b = y[(i + s) % d];
if ((a == 1 && b == 2) || (a == 2 && b == 1)) {
bad = 1;
break;
}
if (a == 0 && b == 0) freeCnt++;
}
if (!bad) {
res += pw2[freeCnt];
if (res >= MOD) res -= MOD;
}
}
return res;
}
vector<int> bad(d), freeCnt(d);
addCorrelation(x, y, 1, 2, bad);
addCorrelation(x, y, 2, 1, bad);
addCorrelation(x, y, 0, 0, freeCnt);
int res = 0;
for (int s = 0; s < d; s++) {
if (bad[s] == 0) {
res += pw2[freeCnt[s]];
if (res >= MOD) res -= MOD;
}
}
return res;
}
int main() {
setIO();
pw2.assign(400005, 1);
for (int i = 1; i < (int)pw2.size(); i++) pw2[i] = (pw2[i - 1] * 2LL) % MOD;
int tc;
cin >> tc;
while (tc--) {
int n;
cin >> n >> T;
halfLen = n / 2;
vector<int> divisors;
for (int i = 1; (ll)i * i <= halfLen; i++) {
if (halfLen % i == 0) {
divisors.push_back(i);
if (i * i != halfLen) divisors.push_back(halfLen / i);
}
}
sort(divisors.begin(), divisors.end());
vector<int> exact(halfLen + 1);
for (int d : divisors) exact[d] = calcPeriod(d);
for (int i = 0; i < (int)divisors.size(); i++) {
int d = divisors[i];
for (int j = i + 1; j < (int)divisors.size(); j++) {
int e = divisors[j];
if (e % d == 0) {
int sub = (int)((ll)exact[d] * (e / d) % MOD);
exact[e] -= sub;
if (exact[e] < 0) exact[e] += MOD;
}
}
}
int ans = 0;
for (int d : divisors) {
ans += exact[d];
if (ans >= MOD) ans -= MOD;
}
cout << ans << char(10);
}
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
const int MOD = 998244353;
const int ROOT = 3;
int modPow(ll a, ll e) {
ll r = 1;
while (e > 0) {
if (e & 1) r = r * a % MOD;
a = a * a % MOD;
e >>= 1;
}
return (int)r;
}
void ntt(vector<int>& a, bool invert) {
int n = (int)a.size();
for (int i = 1, j = 0; i < n; i++) {
int bit = n >> 1;
for (; j & bit; bit >>= 1) j ^= bit;
j ^= bit;
if (i < j) swap(a[i], a[j]);
}
for (int len = 2; len <= n; len <<= 1) {
int wlen = modPow(ROOT, (MOD - 1) / len);
if (invert) wlen = modPow(wlen, MOD - 2);
for (int i = 0; i < n; i += len) {
ll w = 1;
int half = len >> 1;
for (int j = 0; j < half; j++) {
int u = a[i + j];
int v = (int)(a[i + j + half] * w % MOD);
int x = u + v;
if (x >= MOD) x -= MOD;
int y = u - v;
if (y < 0) y += MOD;
a[i + j] = x;
a[i + j + half] = y;
w = w * wlen % MOD;
}
}
}
if (invert) {
int invN = modPow(n, MOD - 2);
for (int& x : a) x = (int)((ll)x * invN % MOD);
}
}
vector<int> pw2;
string T;
int halfLen;
int codeChar(char c) {
if (c == '?') return 0;
if (c == 'a') return 1;
return 2;
}
void addCorrelation(const vector<int>& x, const vector<int>& y, int vx, int vy, vector<int>& dest) {
int d = (int)x.size();
int sz = 1;
while (sz < 3 * d) sz <<= 1;
vector<int> a(sz), b(sz);
for (int i = 0; i < d; i++) a[d - 1 - i] = (x[i] == vx);
for (int i = 0; i < 2 * d; i++) b[i] = (y[i % d] == vy);
ntt(a, false);
ntt(b, false);
for (int i = 0; i < sz; i++) a[i] = (int)((ll)a[i] * b[i] % MOD);
ntt(a, true);
for (int s = 0; s < d; s++) dest[s] += a[d - 1 + s];
}
int calcPeriod(int d) {
vector<int> x(d), y(d);
for (int i = 0; i < halfLen; i++) {
x[i % d] |= codeChar(T[i]);
y[i % d] |= codeChar(T[halfLen + i]);
}
for (int i = 0; i < d; i++) {
if (x[i] == 3 || y[i] == 3) return 0;
}
const int DIRECT_LIMIT = 80;
if (d <= DIRECT_LIMIT) {
int res = 0;
for (int s = 0; s < d; s++) {
int bad = 0, freeCnt = 0;
for (int i = 0; i < d; i++) {
int a = x[i], b = y[(i + s) % d];
if ((a == 1 && b == 2) || (a == 2 && b == 1)) {
bad = 1;
break;
}
if (a == 0 && b == 0) freeCnt++;
}
if (!bad) {
res += pw2[freeCnt];
if (res >= MOD) res -= MOD;
}
}
return res;
}
vector<int> bad(d), freeCnt(d);
addCorrelation(x, y, 1, 2, bad);
addCorrelation(x, y, 2, 1, bad);
addCorrelation(x, y, 0, 0, freeCnt);
int res = 0;
for (int s = 0; s < d; s++) {
if (bad[s] == 0) {
res += pw2[freeCnt[s]];
if (res >= MOD) res -= MOD;
}
}
return res;
}
int main() {
setIO();
pw2.assign(400005, 1);
for (int i = 1; i < (int)pw2.size(); i++) pw2[i] = (pw2[i - 1] * 2LL) % MOD;
int tc;
cin >> tc;
while (tc--) {
int n;
cin >> n >> T;
halfLen = n / 2;
vector<int> divisors;
for (int i = 1; (ll)i * i <= halfLen; i++) {
if (halfLen % i == 0) {
divisors.push_back(i);
if (i * i != halfLen) divisors.push_back(halfLen / i);
}
}
sort(divisors.begin(), divisors.end());
vector<int> exact(halfLen + 1);
for (int d : divisors) exact[d] = calcPeriod(d);
for (int i = 0; i < (int)divisors.size(); i++) {
int d = divisors[i];
for (int j = i + 1; j < (int)divisors.size(); j++) {
int e = divisors[j];
if (e % d == 0) {
int sub = (int)((ll)exact[d] * (e / d) % MOD);
exact[e] -= sub;
if (exact[e] < 0) exact[e] += MOD;
}
}
}
int ans = 0;
for (int d : divisors) {
ans += exact[d];
if (ans >= MOD) ans -= MOD;
}
cout << ans << char(10);
}
}