[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 }
以上是关于[cf794G]Replace All的主要内容,如果未能解决你的问题,请参考以下文章
FragmentTransaction.replace() 淡入过渡显示“幽灵”片段
片段行为:FragmentTransaction::replace() 和反向 backStack 操作