专栏文章

CF794G 题解

CF794G题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mipswphg
此快照首次捕获于
2025/12/03 17:25
3 个月前
此快照最后确认于
2025/12/03 17:25
3 个月前
查看原文
这题其实不难。
Update 2025.3.26:修改题解 Markdown。
Update 2025.3.27:修改题解中错误的式子,并重写逻辑清晰的代码。

Part 1.

首先,考虑没有 ? 如何做。
分两种情况:
  • 字符串 xxyy 长度相等。
  • 字符串 xxyy 长度不相等。

先考虑 xxyy 长度不相等怎么做。
首先,将两个串中共同的字符删除。
比如 ABBAAABABBB,两个串有一个共同的 A,两个共同的 B,删除之后变为 AAABB
那么显然,01 串 sstt 的长度比为 2:32:3
ssab\overline{ab}ttcde\overline{cde}a,b,c,d,ea,b,c,d,e 为长度相等的 01 串。
替换一下 xxyy
ABBAAA 变为 abcdecdeababab\overline{abcdecdeababab}
BABBB 变为 cdeabcdecdecde\overline{cdeabcdecdecde}
如果要让替换后的 01 串相等,那么我们需要保证 a=b=c=d=ea = b = c = d = e
如果我们多观察一下别的 xxyy,使用相同的步骤操作,我们会发现,若 sstt 长度最简比为 a:ba:b,那么那么 s=aEs = aEt=bEt = bE
其中 EE 是一个 01 串。kEkE 表示 kkEE 拼接在一起。
由于 sstt 的长度限制是 nn,那么 EE 的长度限制就是 len=nmax{a,b}len = \lfloor\frac{n}{\max\{a, b\}}\rfloor
我们求出长度小于等于 lenlen 的非空 01 串即为这种情况的答案。
答案 =2len+12= 2^{len + 1} - 2
注意如果 xx 或者 yy 在删除共同字符之后为空,那么无解。

接下来考虑 xxyy 长度相等怎么做。
这种情况还要再细分。
  • xxyy 完全相等。
    显然,sstt 可以任意取。
    答案为 (2n+12)2(2^{n + 1} - 2)^2
  • xxyyA 的数量相等(并且 B 的数量相等)。
    这种情况 sstt长度仍然是可以任取,但是由于 xxyy 不相同,那么必然会有某些位置 xi=x_i = Ayi=y_i = B(或相反),那么 sstt 中必然会有一个串是另一个串的前缀。 然后,举个例子,ss 长度为 66tt 长度为 44。设 s=abcdef,t=abcds = \overline{abcdef}, t = \overline{abcd}
    abcdefabcd\texttt{abcdef{\color{red}{abcd}}}
    abcdabcd\texttt{abcd{\color{red}{abcd}}}
    标红是因为无论后面接上的是 ss 还是 tt,前四个都一定是 abcd\overline{abcd}。那么可以得出 e=a,f=b,a=c,b=de = a, f = b, a = c, b = d
    那么 a=c=ea = c = e 并且 b=d=fb = d = f
    所以 s=ababab,t=ababs = \overline{ababab}, t = \overline{abab}
    简单证明或者感性理解即可得知:sstt 的最长重复单元长度为:gcd(s,t)\gcd(|s|, |t|)
    那么答案即为 a=1nb=1n2gcd(a,b)=k=1n(2k(2j=1nkφj1))\sum\limits_{a = 1}^{n}\sum\limits_{b = 1}^{n}2^{\gcd(a, b)} = \sum\limits_{k = 1}^{n}(2^k(2\sum\limits_{j = 1}^{\lfloor\frac{n}{k}\rfloor}\varphi_j - 1))(推式子见结尾)。
    前缀和处理 i=1nkφi\sum\limits_{i = 1}^{\lfloor\frac{n}{k}\rfloor}\varphi_i 即可。
  • 以上两种情况都不符合。
    显然,对于这种情况,ss 一定等于 tt
    那么答案为 2m+122^{m + 1} - 2
以上解决了无 ? 的情况。

Part 2.

现在解决有 ? 的情况。
分两种情况:
  • 字符串 xxyy 长度相等。
  • 字符串 xxyy 长度不相等。

还是先考虑 xxyy 长度不相等怎么做。
首先,我们计算出 xxyy 中含有的 AB 的差。
cntx,Acnt_{x, A} 表示 xxA 的个数,其他同理。
那么:记 A 个数差 dA=cntx,Acnty,Ad_A = cnt_{x, A} - cnt_{y, A}B 个数差 dB=cntx,Bcnty,Bd_B = cnt_{x, B} - cnt_{y, B}
然后,我们考虑最边界的情况,xx 中的所有 ? 设为 Ayy 中的所有 ? 设为 B
那么:
dA=cntx,Acnty,A+cntx,?d_A = cnt_{x, A} - cnt_{y, A} + cnt_{x, ?}
dB=cntx,Bcnty,Bcnty,?d_B = cnt_{x, B} - cnt_{y, B} - cnt_{y, ?}
接下来,我们考虑修改问号的值会发生什么。
举个例子:
为方便染色,使用 LaTeX\LaTeX
AAB?B?A\texttt{AAB?B?A}
B?ABBB?A\texttt{B?ABBB?A}
变为边界情况:
AABABAA\texttt{AAB{\color{red}{A}}B{\color{red}{A}}A}
BBABBBBA\texttt{B{\color{red}{B}}ABBB{\color{red}{B}}A}
接下来我们以这个状态为基准,我们发现,翻转任意一个标红字母(将 A 变为 B,或相反),对 dAd_AdBd_B 的影响都是一样的。
xx 中的 A 变为 B,会导致 dBd_B 加一,而 A 的数量减一,dAd_A 减一。
若将 yy 中的 B 变为 A,同样会导致 dBd_B 加一,dAd_A 减一。
那么我们可以枚举有多少个字母从基准状态翻转了。
标红字母个数为 cnt?cnt_?,翻转字母数量为 kk,那么总共可能的情况有 Ccnt?kC_{cnt_?}^{k} 种,dA=dAkd_A' = d_A - kdB=dB+kd_B' = d_B + k
然后我们考虑每组 dAd_A'dBd_B' 怎么计算贡献。
如果 dA×dB0d_A' \times d_B' \ge 0,那么则代表两者同号或有一项等于 00,那么就对应了这种情况:
注意如果 xx 或者 yy 在删除共同字符之后为空,那么无解。
只有两者异号,才能产生贡献。
然后两者异号,就可以直接用上面的无 ?x,yx,y 不等长解决。
s,ts,t 长度最简比即为 dBd_B'dAd_A' 的最简比。
fa,bf_{a, b} 表示 dA=ad_A' = adB=bd_B' = b 时的答案。
最终答案为:
i=0cnt?(Ccnt?k×fdAi,dB+i)\sum\limits_{i = 0}^{cnt_?}(C_{cnt_?}^{k}\times f_{d_A - i, d_B + i})
fa,bf_{a, b} 直接求解,O(logn)O(\log n)log\log 来自快速幂和 gcd\gcd
总复杂度 O(nlogn)O(n \log n)

接下来考虑 x,yx,y 长度相等的情况。
我们需要先扫描一遍,判断是否有某种组合,让 x=yx = y,如果有,计算出情况数 cntscnt_s(same)。
再计算出边界情况下的 dAd_AdBd_B
接下来,我们如果需要让两个串 A 的数量相等,那么应当使 dA=0d_A = 0
对应的情况数 cntecnt_e(equal)=Ccnt?dAcnts= C_{cnt_?}^{d_A} - cnt_s,要注意这种情况是严格包含 cntscnt_s 的情况的,所以要减掉。
dA<0d_A < 0dA>cnt?d_A > cnt_?,则 cnte=0cnt_e = 0,因为不可能使两串 A 数量相等。
剩下的情况则为普通情况,数量 cntncnt_n(normal)=2cnt?cntscnte= 2^{cnt_?} - cnt_s - cnt_e
接下来我们一个个计算贡献。
same:相同则任意取:(2n+12)2(2^{n + 1} - 2)^2
equal:长度任意:k=1n(2k(2j=1nkφj1))\sum\limits_{k = 1}^{n}(2^k(2\sum\limits_{j = 1}^{\lfloor\frac{n}{k}\rfloor}\varphi_j - 1))
normal:s=ts = t(2n+12)(2^{n + 1} - 2)
最终答案为:
cnts×(2n+12)2+cnte×k=1n(2k(2j=1nkφj1))+cntn×(2n+12)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)O(n)

接下来讲一下一些式子。
在上面经常会看到 2n+122^{n + 1} - 2 出现。
这个即为长度小于等于 nn 的 01 串的数量。
因为 i=1n2i=2n+12\sum\limits_{i = 1}^{n} 2^i = 2^{n + 1} - 2,小学数学即可证明。

a=1nb=1n2gcd(a,b)\sum\limits_{a = 1}^{n}\sum\limits_{b = 1}^{n}2^{\gcd(a, b)} =k=1naknbkn2k[gcd(a,b)=k]=\sum\limits_{k = 1}^{n}\sum\limits_{a|k}^{n}\sum\limits_{b|k}^{n}2^k[\gcd(a, b) = k] =k=1ni=1nkj=1nk2k[gcd(i,j)=1]=\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] =k=1n2ki=1nkj=1nk[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]
考虑这块的含义:i=1nkj=1nk[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)(i, j),满足 1i,jnk1\le i, j\le\lfloor\frac{n}{k}\rfloor,且 i,ji, j 互质的个数。
首先,我们知道欧拉函数 φi\varphi_i 表示 11ii 中与 ii 互质的数的个数。
那么我们先假定 i<ji < j,那么显然数对个数为 j=2nkφj\sum\limits_{j = 2}^{\lfloor\frac{n}{k}\rfloor}\varphi_j
那么相反的情况:i>ji > j,与上面相同。
然后就是 i=ji = j,只有 (1,1)(1, 1) 符合,贡献为 11
那么原式等于:
k=1n2k(1+2j=2nkφj)\sum\limits_{k = 1}^{n}2^k(1 + 2\sum\limits_{j = 2}^{\lfloor\frac{n}{k}\rfloor}\varphi_j)
=k=1n2k(2j=1nkφj1)=\sum\limits_{k = 1}^{n}2^k(2\sum\limits_{j = 1}^{\lfloor\frac{n}{k}\rfloor}\varphi_j - 1)

要注意取模、爆 long long。
这些都是因为取模和爆 long long WA 的。

代码

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;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...