CF794G Replace All

是之前 XJ 搬过的题,不过我那次听得云里雾里没去订。


先考虑没有问号的做法。

Part 1 全部相等时的计算

对于 A,B 可以自己上下对应的两个串,必定是所有替换都是合法的,只要满足长度限制的串全都是合法的,这个很好判断,并且答案就是两个独立方案的乘积。具体的,是 (i=1n2i)2

那么对于剩下的情况,必然存在某两个字母替换成 01 串后有交的部分,每个相交都对应着某段前缀与后缀的匹配。


Part 2 证明 AB 都可以表示成另一个串的重复出现若干次的形式。

并且,基于某一步贪心,必定可以将原状态消成一个状态,头尾都满足:两个串的其中一个是 A,另一个是 B

那么这样,A,B 中必然有一个是另一个的前缀,并且是后缀。

然后呢?

冷静头脑.jpg

放弃思考.jpg

等等,既然可以这么消

钦定 |A||B|,然后 AB 这个新串,下一步会匹配 A 或者 B,但不管是哪一个 AB 的前缀一定是 B,除非它的长度小于 B

而小于的情况则表明:ABB 的一段前缀。

先不考虑最后的那一步不够的情况,对于前面的,如果一直往后匹配到一个 A,那么会出现:

BB____ BB____

那么就可以把 A 表示成若干个 B 加上一段 B 的前缀的形式。

那么现在就只剩下了两个串 BB 的某一段前缀。

而另一种情况,你会发现,也是符合这个情况的。

在这么不停地转换之后,你会发现:这个过程非常类似求 gcd,一直到留下的两个串长度为倍数关系的时候,才会停止,并且此时所有串都可以表现成一种形式。

按照这个过程,我们可以将 A 串 和 B 串都拆开,拆分成若干段相同的东西。它们两个串都是这个东西的不断重复。

并且依据这个过程,我们发现两个串长度的关系就是在求 gcd 的过程。

所以我们证明了标题,并且知道这个公共串 C 的长度就是 gcd(|A|,|B|)


Part 3 剩余部分的答案计算

那么 AB 的顺序自然就无所谓了,我们需要在意的只有个数。

AB 所拥有的重复的串 C 的个数必须满足:numASA+numBSB 这个东西上下相同(num 表示这个字母的个数,S 表示每个这个字母转化成 C 的个数)。

又发现这样考虑的话影响答案的是上下相差的 A,B 个数。

定义 a= 上面的 A 的个数减去下面的 A 的个数,b 的定义等价,不过是下减上。

a,b 若异号则直接无解。

那么 a|A|=b|B|,并且 |C|=gcd(|A|,|B|)

a,b 都为 0,那么我们可以将上下看做同构,那么答案为:

i=1nj=1n2gcd(i,j)

p=1n2p(i=1npj=1np[gcd(i,j)=1])

p=1n2p( d=1npμ(d)(npd)2 )

这个东西非常好做,你可以整除分块,也就是我写的 O(n) 的,暴力是 O(nlogn),差别不大。

然后考虑 a,b 不为 0 的情况(其中一个为 0 无解)。我们可以先将 a,b 变成 agcd(a,b),bgcd(a,b),这样的话限制是等价的。那么此时 a,b 就已经互质了。

考虑上面那个式子,我们可以得到:|B|gcd(|A|,|B|)=a,|A|gcd(|A|,|B|)=b

Ed=nmax(a,b) 那么答案的计算就是:

i=1Ed2i


接下去,我们考虑怎么去考虑有问号的情况。

对于每一组 a,b,根据上面的分析,它们的答案有三种情况,我们定义 fa,b 表示这个东西:

  • ab>0 时,是一个二的幂次的前缀和,即为 i=1nmax(a,b)2i

  • a=b=0 时,定义为前面所讲的同构的那个式子,莫反处理出来的那个。

  • 然后,在第二种情况下,有一小部分比较特殊,它们上下完全相同,此时的方案计算又要用到第一个式子,就是 (i=1n2i)2

a,b 的定义照旧,不过新增两个变量 x,y 分别表示上面和下面的问号数量。

那么答案就可以表示为:

i=0xj=0yfa+ij,b+(yj)(xi)(xi)(yj)

惊奇地发现 f 这一项只和 ij 有关。

ijfa+(ij),(b+yx)+(ij)j=??(xi)(yj)

k=yxfa+k,(b+yx)+kj=??(xk+j)(yyj)

大致是这样的感觉,后面部分可以容斥然后范雷蒙德卷积搞成两个组合数的差。

具体的 j 的范围,还取决于枚举的 ij 的大小。

这里考虑到,由于 j 的范围不合法只会使得一些照理来说定义为 0 的组合数 (xk+j) 出现,而我刚刚因为将其当做未定义,会使得答案无法计算。

实际应该是可以直接将 j 的枚举范围改为 [0,y] 的,然后范德蒙德卷积卷起来,也就是:(x+yk+y)

那么就做完了。


Part 4 Code

#include <vector> #include <string> #include <stdio.h> #include <iostream> #define LL long long using namespace std; inline LL rin() { LL s=0; bool bj=false; char c=getchar(); for(;(c>'9'||c<'0')&&c!='-';c=getchar()); if(c=='-')bj=true,c=getchar(); for(;c>='0'&&c<='9';c=getchar())s=(s<<1)+(s<<3)+(c^'0'); if(bj)return -s; return s; } const int N=6e5+3; const int M=1e9+7; inline int prpr(int x,int y){return 1LL*x*y%M;} inline int ksm(int x,int y){int ans=1;for(;y;y>>=1){if(y&1)ans=prpr(ans,x);x=prpr(x,x);}return ans;} int pw[N]; int sl[N]; int sr[N]; int mu[N]; bool pri[N]; vector<int>prime; int Gura_do_not_know_my_exist; int CS[N]; inline int cs(int n) { if(CS[n])return CS[n]; int ans=0; for(int l=1,r,nw;l<=n;l=r+1)r=n/(nw=n/l),ans=(ans+prpr(mu[r]-mu[l-1],prpr(nw,nw)))%M; return CS[n]=ans; } inline void init(int n) { pw[0]=1;for(int i=1;i<N;i++)pw[i]=(pw[i-1]<<1)%M; pw[0]=0;for(int i=2;i<N;i++)pw[i]=(pw[i-1]+pw[i])%M; sl[0]=1;for(int i=1;i<N;i++)sl[i]=prpr(sl[i-1],i);sr[N-1]=ksm(sl[N-1],M-2); sr[0]=1;for(int i=N-2;i>=1;i--)sr[i]=prpr(sr[i+1],i+1); mu[1]=1; for(int i=2,now;i<N;i++) { if(!pri[i])prime.push_back(i),mu[i]=-1; for(vector<int>::iterator j=prime.begin();j!=prime.end();j++){if((now=(*j)*i)>=N)break;pri[now]=true;if(i%(*j)==0)break;mu[now]=-mu[i];} mu[i]+=mu[i-1]; } for(int l=1,r,nw;l<=n;l=r+1) { r=n/(nw=n/l); Gura_do_not_know_my_exist+=prpr(pw[r]-pw[l-1],cs(nw)); Gura_do_not_know_my_exist%=M; } return; } inline int C(int a,int b){return (a<b||b<0)?(0):(prpr(sl[a],prpr(sr[b],sr[a-b])));} inline int Gcd(int a,int b){return (!b)?(a):(Gcd(b,a%b));} int n; int ans; string S,T; inline void work(int i,int a,int b,int x,int y) { a+=i;b+=i; if(!a&&!b){ans=(ans+prpr(Gura_do_not_know_my_exist,C(x+y,i+y)))%M;return;} if((1LL*a*b)<=0)return; int c=Gcd(a,b);a/=c;b/=c; ans=(ans+prpr(pw[n/max(a,b)],C(x+y,i+y)))%M; return; } inline bool check(int s,int t) { if(s!=t)return false;for(int i=0;i<s;i++)if(S[i]!=T[i]||S[i]=='?'||T[i]=='?')return false; printf("%d\n",prpr(pw[n],pw[n]));return true; } int main() { int s,t,a,b,x,y;a=b=x=y=0; cin>>S>>T;s=S.length();t=T.length();n=rin();init(n); if(check(s,t))return 0; for(int i=0;i<s;i++){if(S[i]=='A')a++;if(S[i]=='B')b--;if(S[i]=='?')x++;} for(int i=0;i<t;i++){if(T[i]=='A')a--;if(T[i]=='B')b++;if(T[i]=='?')y++;} for(int i=-y;i<=x;i++)work(i,a,b+y-x,x,y); if(s==t) { int cut=1; for(int i=0;i<s;i++) { if(S[i]=='?'&&T[i]=='?'){cut=(cut<<1)%M;continue;} if(S[i]=='?'||T[i]=='?'||S[i]==T[i])continue; cut=0;break; } ans=(ans+prpr(prpr(pw[n],pw[n])-Gura_do_not_know_my_exist,cut))%M; ans=(ans+M)%M; } printf("%d\n",ans); return 0; }

__EOF__

本文作者zjjws
本文链接https://www.cnblogs.com/zjjws/p/14480625.html
关于博主:我就是:究极的 Mad Scientist,凤—凰—院—凶—真da!
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:这么屑的博主有什么声援的必要吗?
posted @   zjjws  阅读(114)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示