codeforces 794G. Replace All

4 篇文章 0 订阅
4 篇文章 0 订阅

题目大意:给两个'A''B''?'组成的串XY,'?'可以是'A'或'B',求所有'?'的情况下,将'A''B'换成两个长度小于n的01串的方案数和。

这好像有一些奥妙重重的性质。

先考虑一个简单的情况:第一个串有a个'A',第二个串有b个'B'。设'A'=>A,'B'=>B。b*|A|=a*|B|,且AB串coprime(是官方题解中的说法,互质,就像辗转相除法一样的感觉)。由于这样的性质,不妨设B=AX,X是一个串。存在AB=BA。

如果AB互质,"AB"="BA",对任何询问都能转换"A..AB..B","A..AB..B"。如果这两个串不同,等价于上面情况,可以直接做,否则即要统计互质的AB串对数,使用容斥解决。

但是如果AB不互质,刚才的方法还是无法解决(此处强调AB是求出来的答案01串)。经过分析,X!=Y时,AB互质,证明略;X==Y时,A==B,容易计算。

至此问题已解决,复杂度O(nlogn+len)。


#include<bits/stdc++.h>
#define P 1000000007
#define N 600005
#define ll long long
using namespace std;
int l1,l2,n,Aa,Ba,Ca,Ab,Bb,Cb,flag,CC;
ll ans,inv[N],sum[N],po[N],fac[N],ifac[N],G;
char a[N],b[N];
int gcd(int x,int y)
{
	return y?gcd(y,x%y):x;
}
ll C(int x,int y)
{
	return fac[y]*ifac[y-x]%P*ifac[x]%P;
}
int main()
{
	scanf("%s%s",a+1,b+1);
	l1=strlen(a+1);
	l2=strlen(b+1);
	scanf("%d",&n);
	/**/
	po[0]=1;
	for (int i=1;i<=600000;i++)
		po[i]=po[i-1]*2%P;
	inv[1]=1;
	for (int i=2;i<=600000;i++)
		inv[i]=(-P/i)*inv[P%i]%P;
	fac[0]=1;ifac[0]=1;
	for (int i=1;i<=600000;i++)
		fac[i]=fac[i-1]*i%P,ifac[i]=ifac[i-1]*inv[i]%P;
	flag=l1==l2;
	for (int i=1;i<=l1;i++)
	{
		if (a[i]=='A') Aa++;
		if (a[i]=='B') Ba++;
		if (a[i]=='?') Ca++;
		flag&=a[i]==b[i]||a[i]=='?'||b[i]=='?';
		if (a[i]=='?'&&b[i]=='?') CC++;
	}
	for (int i=1;i<=l2;i++)
	{
		if (b[i]=='A') Ab++;
		if (b[i]=='B') Bb++;
		if (b[i]=='?') Cb++;
	}
	//calc G 小于n的coprime串对数 gcd (n/d)^2*2^d
	for (int i=1;i<=n;i++)
		sum[i]=(ll)(n/i)*(n/i)%P;
	for (int i=n;i>=1;i--)
	{
		for (int j=2;j<=n/i;j++)
			sum[i]=(sum[i]-sum[i*j])%P;
		G=(G+sum[i]*po[i]%P)%P;
	}
	
	for (int i=-Cb;i<=Ca;i++)
	{
		//a: Aa+u Ba+Ca-u
		//b: Ab+v Bb+Cb-v
		//magic
		//a: Aa-Ab+u-v
		//b: Bb+Cb-Ba-Ca-v+u
		//i=u-v
		
		int A=Aa-Ab+i,B=Bb+Cb-Ba-Ca+i;ll t=C(Cb+i,Ca+Cb);
		if ((ll)A*B<0) continue;
		if (A<=0&&B<=0) A=-A,B=-B;
		if (A==0&&B==0)
		{
			//cout<<G<<endl;
			if (flag)
			{
				ans=(ans+(po[n+1]-2)*(po[n+1]-2)%P*po[CC]%P)%P;
				t=(t-po[CC]+P)%P;
			}
			ans=(ans+t*G)%P;
			continue;
		}
		if (A>B) swap(A,B);
		if (A==0) continue;
		ans=(ans+t*(po[n/(B/gcd(A,B))+1]-2)%P)%P;
		//cout<<A<<' '<<B<<' '<<t<<' '<<ans<<endl;
	}
	printf("%lld\n",(ans+P)%P);
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值