CF765G. Math, math everywhere

题目大意

给出长度为m的数组s和数字N,求多少个数k满足0<=k<N且对于每个i=0~m-1,都有gcd(k+i,N)=1当且仅当si=1

m<=40,N以Πpi^ai的方式给出,n<=5e5

题解

先把01翻转变成在x%p=0的x处填1,填出来的显然按照Πpi为一个周期,所以只需要考虑ai=1的情况最后再乘上Πpi^(ai-1)即可

数字k可以写成模pi的同余组,一个同余组唯一对应一个0~N-1中的数,否则存在<N的%pi=0的数,所以唯一对应

问题变成统计同余组的情况,对于%p=q相当于在数组上按照间隔p的顺序跳着填,类似01010101或者01001001之类的

于是对于p<=m的状压记忆化,>m的因为只会填最多一个所以可以只记录剩余个数dp

发现当p以3或5开头的话状态很多,同时发现当p=29,31,37的时候很类似上面第二部分的dp,所以在做29,31,37的时候把前后m-p位状压中间的记录个数即可,<=23的记忆化,>40的记个数

分三个部分dp,听起来很简单但是细节很多

细节:

1、记忆化23的时候如果用map存时间会炸掉(大概一千七百万多),所以在做23的时候直接转移到第二部分

2、第二部分做29时如果直接循环复杂度为2^22*18*11,所以只做有值的部分,时间变成17000000*11

3、做完29之后状态会增多,如果再按照上面的来搞状态就不止一千多万了,所以第二部分每做完之后都要缩成新的二进制状态

4、缩完之后空间直接存会炸掉(2^22*41),所以要编号

5、缩的时候新开的数组开2^18*23,开原来那么大也会炸掉

code

不知道有没有更简单的做法

#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define low(x) ((x)&-(x))
#define add(a,b) a=((a)+(b))%1000000007
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define mod 1000000007
#define ll long long
//#define file
using namespace std;

int g[79691777],G[6029313],h[41],num[1048576],m,n,M,MM,M2,MM2,id,id2,i,j,k,l,J,K,sum;
ll p[500001],P[41],A,L,L2,L3,LL2,ans,Ans,s1,s2,S1,S2,S;
char st[41];
pair<ll,int> s;
map<ll,int> f[13];
map<ll,int> :: iterator I;

ll qpower(ll a,int b) {ll ans=1; while (b) {if (b&1) ans=ans*a%mod;a=a*a%mod;b>>=1;} return ans;}
int turn(int i,int j,int k,int M,int L2) {return (i*(L2+1)+j)*(M+1)+k;}
ll get(ll t,int x,int y)
{
	int i,ans=0;
	t>>=x;y-=x-1;
	while (y)
	{
		if (y>20) ans+=num[t&(P[20]-1)],y-=20,t>>=20;
		else {ans+=num[t&(P[y]-1)];break;}
	}
	return ans;
}

int main()
{
	#ifdef file
	freopen("CF765G.in","r",stdin);
	#endif
	
	P[0]=1;
	fo(i,1,40) P[i]=P[i-1]*2;
	scanf("%s",st+1);m=strlen(st+1);Ans=1;
	scanf("%d",&n);
	fo(i,1,n) scanf("%lld%lld",&p[i],&A),Ans=(Ans*qpower(p[i],A-1))%mod;
	fd(i,m,1) s1=s1*2+(st[i]-'0'),sum+=!(st[i]-'0');
	
	l=(1ll<<20)-1;
	fo(i,1,l) num[i]=num[i-low(i)]+1;
	sort(p+1,p+n+1);
	L=(1ll<<m)-1;s1^=L;
	
	if (p[1]<=23)
	f[0][0]=1;
	else
	if (p[1]<m)
	M=m-p[1],L2=P[M]-1,g[get(s1,M,p[1]-1)]=1;
	else
	h[sum]=1;
	l=1;
	
	while (l<=n && p[l]<=23)
	{
		for (I=f[l-1].begin(); I!=f[l-1].end(); ++I)
		{
			s=*I;
			for (i=S=0; i<m; i+=p[l]) S|=1ll<<i;
			fo(j,0,p[l]-1)
			{
				s2=s.first|S;
				if ((s2&s1)==s2)
				{
					if (l==n || p[l+1]>23)
					{
						if (l==n || p[l+1]>=m)
						k=get(s2^s1,0,m-1),add(h[k],s.second);
						else
						{M=m-p[l+1];L2=P[M]-1;M2=p[l+1]-M;k=get(s2^s1,M,p[l+1]-1);add(g[turn(s2&(P[M]-1),s2>>p[l+1],k,M2,L2)],s.second);}
					}
					else
					add(f[l][s2],s.second);
				}
				S=(S*2)&L;
			}
		}
		++l;
	}
	if (!M && l<=n && p[l]<m) {printf("0
");return 0;}
	while (l<=n && p[l]<m)
	{
		S=get(s1,M,p[l]-1);S1=s1&(P[M]-1),S2=s1>>p[l];
		fd(i,L2,0)
		{
			fd(j,L2,0)
			{
				fo(k,0,M2)
				{
					id=turn(i,j,k,M2,L2);
					if (g[id])
					{
						s2=g[id];g[id]=0;
						if (l==n || p[l+1]>=m)
						{
							fo(J,0,M-1)
							if ((s1&P[J]) && (s1&P[J+p[l]]))
							K=num[(i|P[J])^S1]+num[(j|P[J])^S2]+k,add(h[K],s2);
							if (k)
							K=num[i^S1]+num[j^S2]+k-1,add(h[K],1ll*s2*k%mod);
							K=num[i^S1]+num[j^S2]+k,add(h[K],1ll*s2*(S-k)%mod);
						}
						else
						{
							fo(J,0,M-1)
							if ((s1&P[J]) && (s1&P[J+p[l]]))
							add(g[turn(i|P[J],j|P[J],k,M2,L2)],s2);
							if (k)
							add(g[turn(i,j,k-1,M2,L2)],1ll*s2*k%mod);
							add(g[turn(i,j,k,M2,L2)],1ll*s2*(S-k)%mod);
						}
					}
				}
			}
		}
		
		++l;
		if (l<=n && p[l]<m)
		{
			MM=m-p[l],LL2=P[MM]-1,MM2=p[l]-MM;
			S1=get(s1,MM,M-1);S2=get(s1,p[l-1],p[l]-1);
			fo(i,0,L2)
			{
				fo(j,0,L2)
				{
					fo(k,0,M2)
					{
						id=turn(i,j,k,M2,L2);
						if (g[id])
						{
							id2=turn(i&(P[MM]-1),j>>(p[l]-p[l-1]),k+(S1-(num[i]-num[i&(P[MM]-1)]))+(S2-(num[j]-num[j>>(p[l]-p[l-1])])),MM2,LL2);
							add(G[id2],g[id]);
						}
					}
				}
			}
			memcpy(g,G,((LL2+1)*(LL2+1)*(MM2+1))*4);
			M=MM,L2=LL2,M2=MM2;
			memset(G,0,((L2+1)*(L2+1)*(M2+1))*4);
		}
	}
	while (l<=n)
	{
		fo(i,0,m)
		{
			if (i)
			add(h[i-1],1ll*h[i]*i%mod);
			h[i]=1ll*h[i]*(p[l]-m+(sum-i))%mod;
		}
		++l;
	}
	
	ans=h[0];
	printf("%lld
",ans*Ans%mod);
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}
原文地址:https://www.cnblogs.com/gmh77/p/13610497.html