CF794G 题解

· · 题解

这题其实不难。

Update 2025.3.26:修改题解 Markdown。

Update 2025.3.27:修改题解中错误的式子,并重写逻辑清晰的代码。

Part 1.

首先,考虑没有 ? 如何做。

分两种情况:

先考虑 xy 长度不相等怎么做。

首先,将两个串中共同的字符删除。

比如 ABBAAABABBB,两个串有一个共同的 A,两个共同的 B,删除之后变为 AAABB

那么显然,01 串 st 的长度比为 2:3

s\overline{ab}t\overline{cde}a,b,c,d,e 为长度相等的 01 串。

替换一下 xy

ABBAAA 变为 \overline{abcdecdeababab}

BABBB 变为 \overline{cdeabcdecdecde}

如果要让替换后的 01 串相等,那么我们需要保证 a = b = c = d = e

如果我们多观察一下别的 xy,使用相同的步骤操作,我们会发现,若 st 长度最简比为 a:b,那么那么 s = aEt = bE

其中 E 是一个 01 串。kE 表示 kE 拼接在一起。

由于 st 的长度限制是 n,那么 E 的长度限制就是 len = \lfloor\frac{n}{\max\{a, b\}}\rfloor

我们求出长度小于等于 len 的非空 01 串即为这种情况的答案。

答案 = 2^{len + 1} - 2

注意如果 x 或者 y 在删除共同字符之后为空,那么无解。

接下来考虑 xy 长度相等怎么做。

这种情况还要再细分。

以上解决了无 ? 的情况。

Part 2.

现在解决有 ? 的情况。

分两种情况:

还是先考虑 xy 长度不相等怎么做。

首先,我们计算出 xy 中含有的 AB 的差。

cnt_{x, A} 表示 xA 的个数,其他同理。

那么:记 A 个数差 d_A = cnt_{x, A} - cnt_{y, A}B 个数差 d_B = cnt_{x, B} - cnt_{y, B}

然后,我们考虑最边界的情况,x 中的所有 ? 设为 Ay 中的所有 ? 设为 B

那么:

d_A = cnt_{x, A} - cnt_{y, A} + cnt_{x, ?} d_B = cnt_{x, B} - cnt_{y, B} - cnt_{y, ?}

接下来,我们考虑修改问号的值会发生什么。

举个例子:

为方便染色,使用 \LaTeX

\texttt{AAB?B?A} \texttt{B?ABBB?A}

变为边界情况:

\texttt{AAB{\color{red}{A}}B{\color{red}{A}}A} \texttt{B{\color{red}{B}}ABBB{\color{red}{B}}A}

接下来我们以这个状态为基准,我们发现,翻转任意一个标红字母(将 A 变为 B,或相反),对 d_Ad_B 的影响都是一样的。

x 中的 A 变为 B,会导致 d_B 加一,而 A 的数量减一,d_A 减一。

若将 y 中的 B 变为 A,同样会导致 d_B 加一,d_A 减一。

那么我们可以枚举有多少个字母从基准状态翻转了。

标红字母个数为 cnt_?,翻转字母数量为 k,那么总共可能的情况有 C_{cnt_?}^{k} 种,d_A' = d_A - kd_B' = d_B + k

然后我们考虑每组 d_A'd_B' 怎么计算贡献。

如果 d_A' \times d_B' \ge 0,那么则代表两者同号或有一项等于 0,那么就对应了这种情况:

注意如果 x 或者 y 在删除共同字符之后为空,那么无解。

只有两者异号,才能产生贡献。

然后两者异号,就可以直接用上面的无 ?x,y 不等长解决。

设 $f_{a, b}$ 表示 $d_A' = a$,$d_B' = b$ 时的答案。 最终答案为: $$\sum\limits_{i = 0}^{cnt_?}(C_{cnt_?}^{k}\times f_{d_A - i, d_B + i})$$ $f_{a, b}$ 直接求解,$O(\log n)$,$\log$ 来自快速幂和 $\gcd$。 总复杂度 $O(n \log n)$。 --- 接下来考虑 $x,y$ 长度相等的情况。 我们需要先扫描一遍,判断是否有某种组合,让 $x = y$,如果有,计算出情况数 $cnt_s$(same)。 再计算出边界情况下的 $d_A$ 和 $d_B$。 接下来,我们如果需要让两个串 `A` 的数量相等,那么应当使 $d_A = 0$。 对应的情况数 $cnt_e$(equal)$= C_{cnt_?}^{d_A} - cnt_s$,要注意这种情况是严格包含 $cnt_s$ 的情况的,所以要减掉。 若 $d_A < 0$ 或 $d_A > cnt_?$,则 $cnt_e = 0$,因为不可能使两串 `A` 数量相等。 剩下的情况则为普通情况,数量 $cnt_n$(normal)$= 2^{cnt_?} - cnt_s - cnt_e$。 接下来我们一个个计算贡献。 same:相同则任意取:$(2^{n + 1} - 2)^2$。 equal:长度任意:$\sum\limits_{k = 1}^{n}(2^k(2\sum\limits_{j = 1}^{\lfloor\frac{n}{k}\rfloor}\varphi_j - 1))$。 normal:$s = t$,$(2^{n + 1} - 2)$。 最终答案为: $$cnt_s\times(2^{n + 1} - 2)^2 + cnt_e \times \sum\limits_{k = 1}^{n}(2^k(2\sum\limits_{j = 1}^{\lfloor\frac{n}{k}\rfloor}\varphi_j - 1)) + cnt_n\times (2^{n + 1} - 2)$$ 前缀和处理欧拉函数,复杂度 $O(n)$。 --- 接下来讲一下一些式子。 在上面经常会看到 $2^{n + 1} - 2$ 出现。 这个即为长度小于等于 $n$ 的 01 串的数量。 因为 $\sum\limits_{i = 1}^{n} 2^i = 2^{n + 1} - 2$,小学数学即可证明。 --- $$\sum\limits_{a = 1}^{n}\sum\limits_{b = 1}^{n}2^{\gcd(a, b)}$$ $$=\sum\limits_{k = 1}^{n}\sum\limits_{a|k}^{n}\sum\limits_{b|k}^{n}2^k[\gcd(a, b) = k]$$ $$=\sum\limits_{k = 1}^{n}\sum\limits_{i = 1}^{\lfloor\frac{n}{k}\rfloor}\sum\limits_{j = 1}^{\lfloor\frac{n}{k}\rfloor}2^k[\gcd(i, j) = 1]$$ $$=\sum\limits_{k = 1}^{n}2^k\sum\limits_{i = 1}^{\lfloor\frac{n}{k}\rfloor}\sum\limits_{j = 1}^{\lfloor\frac{n}{k}\rfloor}[\gcd(i, j) = 1]$$ 考虑这块的含义:$\sum\limits_{i = 1}^{\lfloor\frac{n}{k}\rfloor}\sum\limits_{j = 1}^{\lfloor\frac{n}{k}\rfloor}[\gcd(i, j) = 1]$。 这块即为数对 $(i, j)$,满足 $1\le i, j\le\lfloor\frac{n}{k}\rfloor$,且 $i, j$ 互质的个数。 首先,我们知道欧拉函数 $\varphi_i$ 表示 $1$ 到 $i$ 中与 $i$ 互质的数的个数。 那么我们先假定 $i < j$,那么显然数对个数为 $\sum\limits_{j = 2}^{\lfloor\frac{n}{k}\rfloor}\varphi_j$。 那么相反的情况:$i > j$,与上面相同。 然后就是 $i = j$,只有 $(1, 1)$ 符合,贡献为 $1$。 那么原式等于: $$\sum\limits_{k = 1}^{n}2^k(1 + 2\sum\limits_{j = 2}^{\lfloor\frac{n}{k}\rfloor}\varphi_j)$$ $$=\sum\limits_{k = 1}^{n}2^k(2\sum\limits_{j = 1}^{\lfloor\frac{n}{k}\rfloor}\varphi_j - 1)$$ --- [AC 记录](https://codeforces.com/contest/794/submission/312636725)。 要注意取模、爆 long long。 这些都是因为取模和爆 long long WA 的。 ![](https://cdn.luogu.com.cn/upload/image_hosting/6rsuyapq.png) #### 代码 ```cpp #include<bits/stdc++.h> #define int long long using namespace std; constexpr int N = 1000005, P = 1000000007; string x, y; int n; int Gcd(int a, int b) { if(b == 0) return a; return Gcd(b, a % b); } int ksm(int a, int n) { if(n == 0) return 1; if(n == 1) return a % P; int d = ksm(a, n / 2); d = d * d % P; if(n & 1) d = d * a % P; return d; } int calc(int da, int db) { if(da * db >= 0) return 0; da = abs(da); db = abs(db); int gd = Gcd(da, db); int a = da / gd; int b = db / gd; int len = n / max(a, b); return (ksm(2, len + 1) - 2 + P) % P; } int frac[N], ifrac[N]; int phi[N]; int sumphi[N]; bool not_prime[N]; int plist[N], h; int C(int n, int m) { return frac[n] * ifrac[n - m] % P * ifrac[m] % P; } void init() { frac[0] = 1; for(int i = 1; i < N; i ++) { frac[i] = frac[i - 1] * i % P; } ifrac[N - 1] = ksm(frac[N - 1], P - 2); for(int i = N - 2; i >= 0; i --) { ifrac[i] = ifrac[i + 1] * (i + 1) % P; } not_prime[1] = 1; phi[1] = 1; for(int i = 2; i < N; i ++) { if(!not_prime[i]) { phi[i] = i - 1; plist[++ h] = i; } for(int j = 1; j <= h && plist[j] * i < N; j ++) { not_prime[plist[j] * i] = 1; if(i % plist[j] == 0) { phi[plist[j] * i] = phi[i] * plist[j]; break; } phi[plist[j] * i] = phi[plist[j]] * phi[i]; } } for(int i = 1; i < N; i ++) { sumphi[i] = (sumphi[i - 1] + phi[i]) % P; } return ; } void diff() { int da = 0, db = 0; int cnt = 0; for(int i = 0; x[i]; i ++) { if(x[i] == 'A') da ++; if(x[i] == 'B') db ++; if(x[i] == '?') {cnt ++; da ++; } } for(int i = 0; y[i]; i ++) { if(y[i] == 'A') da --; if(y[i] == 'B') db --; if(y[i] == '?') {cnt ++; db --; } } int ans = 0; for(int k = 0; k <= cnt; k ++) { ans += C(cnt, k) * calc(da - k, db + k) % P; ans %= P; } printf("%d\n", ans); return ; } void same() { int cnts = 1, cnte = 0, cntn = 0; int da = 0, db = 0; int cnt = 0; for(int i = 0; x[i]; i ++) { if(x[i] == 'A') da ++; if(x[i] == 'B') db ++; if(x[i] == '?') {da ++; cnt ++; } if(y[i] == 'A') da --; if(y[i] == 'B') db --; if(y[i] == '?') {db --; cnt ++; } if(x[i] != y[i] && x[i] != '?' && y[i] != '?') { cnts = 0; } else if(x[i] == y[i] && x[i] == '?') { cnts *= 2; cnts %= P; } } cnte = (da < 0 || da > cnt) ? 0 : (C(cnt, da) - cnts + P) % P; cntn = (ksm(2, cnt) - cnts - cnte + 2 * P) % P; int sume = 0, pw = 1; for(int k = 1; k <= n; k ++) { pw = pw * 2 % P; sume += pw * ((2 * sumphi[n / k] % P - 1 + P) % P) % P; sume %= P; } int ans = { cnts * ksm((ksm(2, n + 1) - 2 + P) % P, 2) % P + cnte * sume + cntn * (ksm(2, n + 1) - 2 + P) % P } ; ans %= P; printf("%d\n", ans); return ; } signed main() { init(); cin >> x >> y >> n; if(x.size() == y.size()) { same(); } else { diff(); } return 0; } ```