【学习笔记】CF794G Replace All

*3400的难度,比较恶心的推式子题。

第一个结论,对于给定的串 s , t s,t s,t,若 ∣ s ∣ ≠ ∣ t ∣ |s|\ne |t| s=t,并且其拼接完全相同(至少有一个位置不一样),那么一定可以把 s , t s,t s,t拆分成长度为 gcd ⁡ ( ∣ s ∣ , ∣ t ∣ ) \gcd(|s|,|t|) gcd(s,t)的串的拼接。于是我们不关心这个串具体长什么样,只要最后拆分出来的数目一样就好了。因此方案数为 2 gcd ⁡ ( ∣ s ∣ , ∣ t ∣ ) 2^{\gcd(|s|,|t|)} 2gcd(s,t)。(这个地方刚开始推错了,后面又改对了,不过没过大样例的原因好像是有些情况没有特判到。)这个结论还是非常好猜的。

换一个角度理解,其实也可以看成 p 1 = s gcd ⁡ ( ∣ s ∣ , ∣ t ∣ ) , p 2 = t gcd ⁡ ( ∣ s ∣ , ∣ t ∣ ) p_1=\frac{s}{\gcd(|s|,|t|)},p_2=\frac{t}{\gcd(|s|,|t|)} p1=gcd(s,t)s,p2=gcd(s,t)t,其中 gcd ⁡ ( p 1 , p 2 ) = 1 \gcd(p_1,p_2)=1 gcd(p1,p2)=1,这样同样是不重不漏的。

然后我们着手处理第二个部分。假设第一个串拆分出了 A + i A+i A+i p 1 p_1 p1 B − i B-i Bi p 2 p_2 p2,第二个串拆分出了 C + j C+j C+j p 1 p_1 p1 D − j D-j Dj p 2 p_2 p2。其中 i ∈ [ 0 , C 1 ] , j ∈ [ 0 , C 2 ] i\in [0,C_1],j\in [0,C_2] i[0,C1],j[0,C2]

不难得出等式: p 1 ( A + i ) + p 2 ( B − i ) = p 1 ( C + j ) + p 2 ( D − j ) p_1(A+i)+p_2(B-i)=p_1(C+j)+p_2(D-j) p1(A+i)+p2(Bi)=p1(C+j)+p2(Dj)

解得 i − j = p 1 C + p 2 D − p 1 A − p 2 B p 1 − p 2 i-j=\frac{p_1C+p_2D-p_1A-p_2B}{p_1-p_2} ij=p1p2p1C+p2Dp1Ap2B为定值。这是个令人兴奋的结果,因为我们记这个定值为 s s s,那么这部分方案数是 ∑ ( C 1 i ) ( C 2 i − s ) = ∑ ( C 1 i ) ( C 2 C 2 + s − i ) = ( C 1 + C 2 C 2 + s ) \sum \binom{C_1}{i}\binom{C_2}{i-s}=\sum \binom{C_1}{i}\binom{C_2}{C_2+s-i}=\binom{C_1+C_2}{C_2+s} (iC1)(isC2)=(iC1)(C2+siC2)=(C2+sC1+C2)

用上面式子直接算,复杂度 O ( n 2 ) O(n^2) O(n2)

下一步非常运气。我们可以把式子写成: p 1 ( s + M ) = p 2 ( s + N ) p_1(s+M)=p_2(s+N) p1(s+M)=p2(s+N)。首先让 s + M , s + N s+M,s+N s+M,s+N互质,又因为 p 1 , p 2 p_1,p_2 p1,p2互质,所以 p 1 = s + N , p 2 = s + M p_1=s+N,p_2=s+M p1=s+N,p2=s+M,然后就做完了???

复杂度 O ( n ) O(n) O(n)

最后还是讲一下最难的部分,也就是算重的问题。如果处理的不好就会很麻烦。

首先我们计算 s + M ≠ 0 s+M\ne 0 s+M=0 s + N ≠ 0 s+N\ne 0 s+N=0的情形。应当注意到,此时 A A A或者 B B B的数目不相等,如果 ∣ s ∣ ≠ ∣ t ∣ |s|\ne |t| s=t那么必然出现交叉,也就是说 s , t s,t s,t一定能拆成 gcd ⁡ ( ∣ s ∣ , ∣ t ∣ ) \gcd(|s|,|t|) gcd(s,t),按上式算即可。如果 ∣ s ∣ = ∣ t ∣ |s|=|t| s=t,那么意味着 s = t s=t s=t,只要判断 ∣ a ∣ = ∣ b ∣ |a|=|b| a=b即可。

然后我们计算 s + M = 0 , s + N = 0 s+M=0,s+N=0 s+M=0,s+N=0的情形。同理,如果 ∣ s ∣ ≠ ∣ t ∣ |s|\ne |t| s=t,并且 a ≠ b a\ne b a=b,那么还是按上式算,显然此时 p 1 , p 2 p_1,p_2 p1,p2可以取任意值,不过因为 s s s是定值,所以可以把组合数提出来,相当于是算 ( ( C 1 + C 2 C 2 + s ) − 2 t o t ) ∑ i ≠ j 2 gcd ⁡ ( i , j ) (\binom{C_1+C_2}{C_2+s}-2^{tot})\sum_{i\ne j}2^{\gcd(i,j)} ((C2+sC1+C2)2tot)i=j2gcd(i,j),这个可以用数论函数预处理就不说了。最后如果 ∣ s ∣ = ∣ t ∣ |s|=|t| s=t,并且 a ≠ b a\ne b a=b,那么意味着 s = t s=t s=t,相当于是 ( ( C 1 + C 2 C 2 + s ) − 2 t o t ) ∑ i ≤ n 2 i (\binom{C_1+C_2}{C_2+s}-2^{tot})\sum_{i\le n}2^i ((C2+sC1+C2)2tot)in2i。注意这一大类还应该考虑 a = b a=b a=b的情形,也就是 2 t o t ( ∑ i ≤ n 2 i ) 2 2^{tot}(\sum_{i\le n}2^i)^2 2tot(in2i)2

不得不承认最后这一步容斥还是很高妙的。考场上能把这个部分想清楚也确实不容易

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
const int N=2e6+5;
string a,b;
int n;
ll f[N],sum[N],res,fac[N],inv[N],A,B,C,D,C1,C2;
ll phi[N];
int prime[N/10],cnt;
bool vis[N];
ll fpow(ll x,ll y=mod-2){
    ll z(1);
    for(;y;y>>=1){
        if(y&1)z=z*x%mod;
        x=x*x%mod;
    }
    return z;
}
void init(int n){
    f[0]=fac[0]=1;for(int i=1;i<=n;i++)f[i]=f[i-1]*2%mod,sum[i]=(f[i]+sum[i-1])%mod,fac[i]=fac[i-1]*i%mod;
    inv[n]=fpow(fac[n]);for(int i=n;i>=1;i--)inv[i-1]=inv[i]*i%mod;
    phi[1]=1;
    for(int i=2;i<=n;i++){
        if(!vis[i]){
            prime[++cnt]=i,phi[i]=i-1;
        }
        for(int j=1;j<=cnt&&prime[j]<=n/i;j++){
            vis[i*prime[j]]=1;
            if(i%prime[j]==0){
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }
            phi[i*prime[j]]=phi[i]*(prime[j]-1);
        }
    }
    for(int i=2;i<=n;i++){
        phi[i]=(phi[i-1]+phi[i])%mod;
    }
}
ll binom(int x,int y){
    if(x<y||y<0)return 0;
    assert(x>=y),assert(y>=0);
    return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
void add(ll &x,ll y){
    x=(x+y)%mod;
}
int gcd(int x,int y){
    return y==0?x:gcd(y,x%y);
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>a>>b>>n;
    for(int i=0;i<a.size();i++){
        if(a[i]=='?')C1++,B++;
        else if(a[i]=='A')A++;
        else B++;
    }
    for(int i=0;i<b.size();i++){
        if(b[i]=='?')C2++,D++;
        else if(b[i]=='A')C++;
        else D++;
    }
    init(max((int)a.size()+(int)b.size(),2*n));
    for(ll s=-C2;s<=C1;s++){
        ll x=A-C+s,y=D-B+s;
        if(x==0&&y==0){
            assert(a.size()==b.size());
            int ok=1,tot=0;
            for(int i=0;i<a.size();i++){
                if(a[i]!='?'&&b[i]!='?'&&a[i]!=b[i])ok=0;
                if(a[i]=='?'&&b[i]=='?')tot++;
            }
            if(ok){
                for(int i=1;i<=n;i++){
                    add(res,f[i]*(phi[n/i]-1)%mod*(binom(C1+C2,C2+s)-f[tot])%mod*2);
                }
                add(res,(binom(C1+C2,C2+s)-f[tot])*sum[n]);
                add(res,f[tot]*sum[n]%mod*sum[n]);
            }
            else{
                for(int i=1;i<=n;i++){
                    add(res,f[i]*(phi[n/i]-1)%mod*binom(C1+C2,C2+s)%mod*2);
                }
                add(res,binom(C1+C2,C2+s)*sum[n]);
            }
        }
        else if(x!=0&&y!=0){
            if(a.size()==b.size()){
                add(res,binom(C1+C2,C2+s)*sum[n]);
            }
            if(!((x>0)^(y>0))&&x!=y){
                x=abs(x),y=abs(y);
                ll z=gcd(x,y);
                x/=z,y/=z;
                assert(gcd(x,y)==1);
                add(res,binom(C1+C2,C2+s)*sum[min(n/y,n/x)]);
            }
        }
    }
    cout<<(res+mod)%mod;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值