[cf794G]Replace All

Posted PYWBKTDA

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[cf794G]Replace All相关的知识,希望对你有一定的参考价值。

(本文中所有字符串都指非空字符串)

第一部分:分析性质

先考虑$c$和$d$中没有"?"的情况——

若$c=d$,显然任意$(s,t)$都可行,答案即$(2^{n+1}-2)^{2}$

下面来考虑$c\\ne d$的情况,先来定义非空串$s$和$t$互素:

$s$和$t$互素当且仅当记$d=\\gcd(|s|,|t|)$,有$\\forall 0\\le i<d,s_{i}=t_{i}$以及$s_{i}=s_{i+d},t_{i}=t_{i+d}$

(即两者都以$d$为循环节长度,且循环节相同)

结论:若$(s,t)$对于$c\\ne d$是”好“的,必要条件为$s$和$t$互素

对$|s|+|t|$归纳,来证明此结论在$|s|+|t|\\le k$时成立,则$|s|+|t|=k+1$时也成立

删去$c$和$d$的最长公共前缀,此时两者开头字母不同,对$s$和$t$的长度分类讨论:

1.若$|s|=|t|$,显然即有$s=t$,即互素

2.若$|s|<|t|$,那么$s$必然为$t$的前缀,且设$t=s+t\'$,不妨将$c$和$d$中的$B$替换为$AB$,得到新串$c\'$和$d\'$

不难证明$c\'\\ne d\'$(两者第2个字符必然不同)且$|s|+|t\'|\\le k$,由于$\\gcd(|s|,|t|)=\\gcd(|s|,|t|-|s|)$,根据归纳可以得到$s$和$t$互素

3.若$|s|>|t|$,与第2种情况类似,这里就不具体分析了

另外,对于初始条件,即$|s|+|t|=2$时,显然有$|s|=|t|=1$,因此$s=t$,即互素

另一方面,当$s$和$t$互素,最终的两个串也具有相同的循环节,因此只需要长度相同即可

第二部分:分类讨论

更具体的,记$a_{c},b_{c},a_{d},b_{d}$分别表示对应串中$A$和$B$的个数,即要$a_{c}|s|+b_{c}|t|=a_{d}|s|+b_{d}|t|$

根据上述分析,以上两个条件即充要条件,下面来考虑答案——

对$a_{c},b_{c},a_{d},b_{d}$的大小分类讨论,由于$|s|,|t|>1$,仅有以下三种情况:

1.$a_{c}=a_{d}$且$b_{c}=b_{d}$

2.$a_{c}>a_{d}$且$b_{c}<b_{d}$

3.$a_{c}<a_{d}$且$b_{c}>b_{d}$(这种情况与第2种类似,以下不具体分析)

在第一种情况下,我们可以通过交换使$c=d$,那么条件仅包含$s$和$t$互素

根据定义,枚举$|s|$和$|t|$,对应的串即有$2^{\\gcd(|s|,|t|)}$种

枚举$\\gcd$,进行莫比乌斯反演,即$\\sum_{d=1}^{n}2^{d}\\sum_{g=1}^{\\lfloor\\frac{n}{d}\\rfloor}\\mu(g)(\\lfloor\\frac{n}{dg}\\rfloor)^{2}$

根据调和级数,暴力计算即可,时间复杂度为$o(n\\log n)$

在第二种情况下,同样通过交换,使得$a_{d}$个$A$和$b_{c}$个$B$抵消,最终$c$为$a_{c}-a_{d}$个$A$、$d$为$b_{d}-b_{c}$个$B$

另一方面,前者条件即可以写作$(a_{c}-a_{d})|s|=(b_{d}-b_{c})|t|$,令$g=\\gcd(a_{c}-a_{d},b_{d}-b_{c})$,则$|s|$和$|t|$可以被描述为$|s|=\\frac{b_{d}-b_{c}}{g}k$以及$|t|=\\frac{a_{c}-a_{d}}{g}k$(其中$k\\in Z^{+}$,有$\\gcd(|s|,|t|)=k$)

考虑枚举$1\\le k\\le \\lfloor\\frac{ng}{\\max(a_{c}-a_{d},b_{d}-b_{c})}\\rfloor$,每一次的答案即$2^{k}$,求和后即$2^{\\lfloor\\frac{ng}{\\max(a_{c}-a_{d},b_{d}-b_{c})}\\rfloor}-2$

第三部分:统计答案

令$c\'$和$d\'$为最终的串,$a_{c\'},b_{c\'},a_{d\'},b_{d\'}$定义类似,再定义$q_{c}$和$q_{d}$为$c$和$d$中"?"的个数

枚举最终的$k=a_{c\'}-a_{d\'}$,显然即可确定$b_{d\'}-b_{c\'}=|d|-|c|+k$,然后根据分类讨论,算出此时的答案

(对于$c=d$的情况,暂时将答案看作分类讨论中的第一种情况)

下面,来统计有多少种"?"的填法能取到这个$k$,枚举$c$填了$i$个$a$,那么$d$中就要填$a_{c}+i-k-a_{d}$个$a$,进而根据组合数,即$\\sum_{i}{q_{c}\\choose i}{q_{d}\\choose a_{c}+i-k-a_{d}}$(关于$i$的范围,实际上当$i$过大或过小该式即为0)

考虑将${q_{c}\\choose i}$变形为${q_{c}\\choose q_{c}-i}$,根据组合意义,即在$q_{c}+q_{d}$个数中选$q_{c}+a_{c}-k-a_{d}$个数(枚举了前$q_{c}$个数中选的数个数$i$),因此即${q_{c}+q_{d}\\choose q_{c}+a_{c}-k-a_{d}}$

由此,将方案数乘上对应的答案并对所有$k$累加即可

令$\\Delta=(2^{n+1}-2)^{2}-\\sum_{d=1}^{n}2^{d}\\sum_{g=1}^{\\lfloor\\frac{n}{d}\\rfloor}\\mu(g)(\\lfloor\\frac{n}{dg}\\rfloor)^{2}$,即每一次$c=d$时的答案差,乘上$c=d$的方案数并加入原答案中来修改$c=d$的答案即可

总复杂度即为$o(n\\log n)$,瓶颈是分类讨论中的第一种情况以及求最大公约数

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 300005
 4 #define mod 1000000007
 5 int n,ac,bc,qc,ad,bd,qd,C1,C2,ans,mi[N<<1],fac[N<<1],inv[N<<1],mu[N],p[N],vis[N];
 6 char s1[N],s2[N];
 7 int sqr(int k){
 8     return 1LL*k*k%mod;
 9 }
10 int C(int n,int m){
11     return 1LL*fac[n]*inv[m]%mod*inv[n-m]%mod;
12 }
13 int gcd(int x,int y){
14     if (!y)return x;
15     return gcd(y,x%y);
16 }
17 int main(){
18     mi[0]=fac[0]=inv[0]=inv[1]=1;
19     for(int i=1;i<(N<<1);i++)mi[i]=2*mi[i-1]%mod;
20     for(int i=1;i<(N<<1);i++)fac[i]=1LL*i*fac[i-1]%mod;
21     for(int i=2;i<(N<<1);i++)inv[i]=1LL*(mod-mod/i)*inv[mod%i]%mod;
22     for(int i=1;i<(N<<1);i++)inv[i]=1LL*inv[i-1]*inv[i]%mod;
23     mu[1]=1;
24     for(int i=2;i<N;i++){
25         if (!vis[i]){
26             p[++p[0]]=i;
27             mu[i]=-1;
28         }
29         for(int j=1;(j<=p[0])&&(i*p[j]<N);j++){
30             vis[i*p[j]]=1;
31             if (i%p[j])mu[i*p[j]]=-mu[i];
32             else{
33                 mu[i*p[j]]=0;
34                 break; 
35             }
36         }
37     }
38     scanf("%s%s%d",s1,s2,&n);
39     int l1=strlen(s1),l2=strlen(s2);
40     for(int i=0;i<l1;i++){
41         if (s1[i]==\'A\')ac++;
42         if (s1[i]==\'B\')bc++;
43         if (s1[i]==\'?\')qc++;
44     }
45     for(int i=0;i<l2;i++){
46         if (s2[i]==\'A\')ad++;
47         if (s2[i]==\'B\')bd++;
48         if (s2[i]==\'?\')qd++;
49     }
50     C1=sqr(mi[n+1]+mod-2);
51     for(int i=1;i<=n;i++){
52         int s=0;
53         for(int j=1;j<=n/i;j++)s=(s+1LL*(mu[j]+mod)*sqr(n/(i*j)))%mod;
54         C2=(C2+1LL*mi[i]*s)%mod;
55     }
56     for(int i=ac-(ad+qd);i<=(ac+qc)-ad;i++){
57         int s=C(qc+qd,qc+ac-i-ad),j=l2-l1+i;
58         if ((i>0)&&(j<=0)||(i<0)&&(j>=0)||(!i)&&(j))continue;
59         if (!i)ans=(ans+1LL*s*C2)%mod;
60         else{
61             int g=gcd(abs(i),abs(j));
62             ans=(ans+1LL*s*(mi[n/(max(abs(i),abs(j))/g)+1]+mod-2))%mod;
63         }
64     }
65     if (l1==l2){
66         int s=1;
67         for(int i=0;i<l1;i++){
68             if ((s1[i]!=\'?\')&&(s2[i]!=\'?\')&&(s1[i]!=s2[i]))s=0;
69             else{
70                 if ((s1[i]==\'?\')&&(s2[i]==\'?\'))s=s*2%mod;
71             }
72         }
73         ans=(ans+1LL*(C1+mod-C2)*s)%mod;
74     }
75     printf("%d",ans);
76 } 
View Code

 

以上是关于[cf794G]Replace All的主要内容,如果未能解决你的问题,请参考以下文章

FragmentTransaction.replace() 淡入过渡显示“幽灵”片段

使用 replace() 时片段未替换

调用 replace() 方法后片段闪烁/闪烁

片段行为:FragmentTransaction::replace() 和反向 backStack 操作

codeforces CF920F SUM and REPLACE 线段树 线性筛约数

CF920F SUM and REPLACE 题解