Codeforces Round #414 (Div1+Div2) G Replace All (组合数学)

考虑给定两个M、N串的情况:
定义:两个01串 S,T(|S||T|) coprime 的当且仅当 S=T 。或者如果 S T的一个前缀,并令 T=S+X ,如果 S,X coprime 的,那么 S,T 也是 coprime 的。

引理1:如果两个串 S,T(|S||T|) coprime 的,则 S+T=T+S

证明:我们对 |T| 进行数学归纳。
|T|=1 ,则 |S|=1 ,由于 S,T coprime 的,则 S=T ,显然 S+T=T+S ,命题成立。
不妨设当 |T|k 时命题成立。
|T|=k+1 时,若此时 |S|=k+1 ,则命题成立。
|S|=x<k+1 ,则 S T的一个前缀,设 T=S+X ,显然 |X|,|S|k
则我们要证 S+S+X=S+X+S ,即 S+X=X+S 。由于 S,X coprime 的,所以上式成立,所以当 |T|=k+1 时成立
原命题得证

引理2: MN ,则 S,T 一定是 coprime 的。
证明方法同上

然后继续分析固定 M N的答案情况。
如果 M=N ,那么任意一个 01 串都可以。
如果 MN ,由于引理1和引理2,我们可以将串 M N进行排序,使得 A 全在前面,B全在后面。我们删去两个串的最长公共前缀,我们用 (MA,MB) 来表示删去最长公共前缀后串 M 拥有的A B 的个数。
如果MA>NA,MB>NB显然无解。
先考虑 MA>NA,MB<NB ,设 x=MANA,y=NBMB 。现在我们需要让 xS=yT ,令 T=S+X ,显然 x>y ,那么代换后可得 (xy)S=yX ,不断如此进行下去可以得到 (1,1) 和串 XX 。反观这个过程,我们实际上是 (yXX,xXX) 。所以我们只要找到合法的 XX 即可。

如果 (MA,MB)=(NA,NB) 那么任意一组 coprime S T都满足情况。我们需要计算这样的个数。假设 |S|=p,|T|=q ,那么方案数为 2gcd(p,q) ,证明方法同上。

所以我们只需要计算 2gcd(p,q),1p,qN
假设 cnt[i] 表示的是 i|gcd(p,q) (p,q) 对数, ans[i] 表示的是 gcd(p,q)=i 的对数,那么

ans[i]=j=1Ni(μ(j)cnt[ij])

我们总结下前面的推导。
dA=MANA,dB=MBNB
如果 dA=dB=0 ,则答案为 ans[N]
如果 dA,dB<0 dA,dB>0 无解
否则,设 p=|dA|,q=|dB|,d=gcd(p,q) ,那么 ans=2Nmax(p/d,q/d)+12

下来考虑问题的原版本
有上述分析可知,对于确定串的方案数如果 MN ,仅和 dA dB 相关。所以我们只要能统计出这个的不同方案即可。
假设一开始 M N串中 A,B 出现的次数之差为 (p,q) M,N 串中问号的个数为 a,b
假设 M,N 中分别将 x,y 个问号替换为 A 。那么新的差为(p+(xy),q+(ab)(xy)),为了简化我们将 q+(ab) q 来代替。则差为(p+(xy),q(xy)) x 的范围为[0,a] y 的范围为[0,b]
d=xy ,我们只要枚举 d ,并且计算出造成这个d的方案数和其对应的答案即可。

对于一个确定的 d ,我们计算造成这个d的方案数

(ax)(by)=(ax)(bby)=(ax)(bb+dx),0xa
这个和为 (a+bb+d)

在最后我们利用DP考虑下两个串相同的方案数即可。至此,该问题结束。

第一次写这么长的题解,完结撒花!

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<bitset>

using namespace std;
typedef long long LL;
const LL mod=1e9+7;
string s;
string t;
int n;
int l1,l2;
LL pow_mod(LL a,LL e)
{
    LL res=1;
    for (;e;a=a*a%mod,e>>=1) if (e&1) res=res*a%mod;
    return res;
}
int miu[610000];
int prim[610000],primm;
bool valid[610000];
void mobius(int N)
{
    miu[1]=1;
    for (int i=2;i<=N;i++)
    {
        if (!valid[i]) prim[++primm]=i,miu[i]=-1;
        for (int j=1;j<=primm&&i*prim[j]<=N;j++)
        {
            valid[i*prim[j]]=1;
            if (i%prim[j]==0)
            {
                miu[i*prim[j]]=0;
                break;
            }
            else miu[i*prim[j]]=-miu[i];
        }
    }
}
LL now;
int na,nb,ma,mb,nn,mm;
LL cnt[310000],ans[310000];
LL res,all;
LL f[610000],ff[610000],inv[610000];
LL C(int n,int m)
{
    LL res=(f[n]*inv[m])%mod;
    res=(res*inv[n-m])%mod;
    return res;
}
LL gcd(LL a,LL b)
{
    return b==0?a:gcd(b,a%b);
}
int main()
{
    mobius(600000);
    cin>>s;
    cin>>t;
    cin>>n;
    l1=s.size();
    l2=t.size();
    if (l1==l2)
    {
        now=1;
        for (int i=0;i<l1;i++)
            if (s[i]=='A')
            {
                if (t[i]=='A'||t[i]=='?');
                else now=0;
            }
            else if (s[i]=='B')
            {
                if (t[i]=='B'||t[i]=='?');
                else now=0;
            }
            else 
            {
                if (t[i]=='A'||t[i]=='B');
                else now=now*2%mod;
            }
        if (now)
        {
            LL tmp=(pow_mod(2,n+1)-2+mod)%mod;
            tmp=tmp*tmp%mod;
            res=tmp*now%mod;
        }
    }
    for (int i=0;i<l1;i++)
        if (s[i]=='A') na++;
        else if (s[i]=='B') nb++;
        else nn++;
    for (int i=0;i<l2;i++)
        if (t[i]=='A') ma++;
        else if (t[i]=='B') mb++;
        else mm++;
    f[0]=1;
    for (int i=1;i<=600010;i++)
        f[i]=(f[i-1]*i)%mod;
    ff[1]=ff[0]=inv[1]=inv[0]=1;  
    for (int i=2;i<=600010;i++)
    {
        inv[i]=(LL)(mod-mod/i)*inv[mod%i]%mod;
        ff[i]=inv[i];
    }
    for (int i=2;i<=600010;i++)
        inv[i]=(inv[i-1]*inv[i])%mod;
    for (int i=1;i<=n;i++)
        cnt[i]=LL(n/i)*(LL)(n/i);
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=n/i;j++)
            ans[i]+=(LL)miu[j]*cnt[i*j];
        all+=ans[i]*pow_mod(2,i);
        all%=mod;
    }
    int p=na-ma,q=nb-mb+(nn-mm);
    for (int i=-mm;i<=nn;i++)
    {
        LL tmp=C(nn+mm,mm+i);
        int np=p+i,nq=q-i;
        if (np==0&&nq==0) tmp=(tmp-now+mod)%mod;
        if (np==nq&&np==0) res=(res+tmp*all)%mod;
        else if (np<=0&&nq<=0||np>=0&&nq>=0);
        else 
        {
            int d=gcd(np,nq);
            np/=d,nq/=d;
            tmp=tmp*(pow_mod(2,n/max(abs(np),abs(nq))+1)-2+mod)%mod;
            res=(res+tmp)%mod;
        }
    }
    cout<<res<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值