bzoj5019: [Snoi2017]遗失的答案
我觉得可能很多人的做法都很傻逼 这毒瘤路牌好好的把我都搞懵逼了
首先先考虑质因数分解G、L,选出的数中质因子的最小次幂和最大次幂要分别等于G、L对应质因子的次幂
可以发现L的质因子个数不会超过8个,n之内满足条件的数的个数不会超过800,考虑状压DP
当没有必选x的限制时,f[i][j][k]表示前i个合法的数,每个质因数下界是否满足,上界是否满足。预处理每个合法的数是否顶上下界,转移显然,实际上第一维可以省去,我做的时候直接把后两维并一起了
现在思考询问,对于固定x要选如何快速得到,想法是直接预处理所有x的答案。
考虑补集转化,求不带x的方案数
有一种非常毒瘤的做法就是分别出来前缀和后缀,询问的时候直接FWT做或卷积,预处理的空间又大跑得又龟除了完全不动脑没有什么优点
不过看到了一种很有意思很优美的分治做法,先进行[l,mid]的影响,然后去递归得到[mid+1,r]的答案,做完以后通过记录上一层的答案重新进行当前层,进行[mid+1,r]的影响,然后递归得到[l,mid]的答案,这个东西理论的时空复杂度应该是O(len*loglen*(2^plen)^2)略小于FWT
我自己的做法是这样的:(不用补集转化)考虑对于必选x,把它的对应的状态用zt表示,那么现在要求的是li^zt的每个超集不包含x的方案数。不难发现,除非是满集,否则li^zt的超集一定不包含x,这里先预处理。考虑如何把满集的贡献计算。只需要去掉必须用x的方案,那么x是可有可无的,直接减除二就可以了。
形式化的:li^zt的每个超集不包含x的方案数 = 超集中非满集不包含x的方案数 + 满集不包含x的方案数
= 超集中非满集的方案数 + (满集的方案数 - 必须用x的方案数)/2
= 超集中非满集的方案数 + (满集的方案数 - 超集中非满集的方案数)/2
枚举子集统计一下超集和(不含满集)就完了
至于网上算至少然后带个容斥系数的,您就图个乐子看吧
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #include<map> #define ft first #define sd second using namespace std; const int _=1e2; const int taxp=8; const int maxp=taxp+2; const int maxz=(1<<2*taxp)+_; const int maxn=8e2+10; typedef long long LL; typedef pair<int,int> pp; const int mod=1e9+7; inline int ad(int x,int y){return (x>=mod-y)?x-mod+y:x+y;} inline int re(int x,int y){return (x<y)?x-y+mod:x-y;}; int n,G,L; int plen,p[maxp],q[2*maxp]; void divi() { plen=0; int K=L; for(int i=2;i*i<=K;i++) if(K%i==0){p[++plen]=i;while(K%i==0)K/=i;} if(K!=1)p[++plen]=K; sort(p+1,p+plen+1); K=L; for(int i=1;i<=plen;i++) if(K%p[i]==0){while(K%p[i]==0)K/=p[i],q[i+plen]++;} K=G; for(int i=1;i<=plen;i++) if(K%p[i]==0){while(K%p[i]==0)K/=p[i],q[i]++;} } int len;pp num[maxn]; map<int,int>leg; void getnum(int k,int d,int s) { if(k==plen+1){num[++len]=make_pair(d,s);return ;} for(int i=q[k],g=int(pow(p[k],q[k])); i<=q[plen+k]; i++,g*=p[k]) if((LL)d*g<=n)getnum(k+1,d*g, s | ((i==q[k])?(1<<k-1):0) | ((i==q[plen+k])?(1<<plen+k-1):0) ); } int f[maxz],d[maxz]; void DP() { f[0]=1; int li=(1<<plen*2)-1; for(int i=0;i<len;i++) for(int zt=li;zt>=0;zt--) if(f[zt])f[zt|num[i+1].sd]=ad(f[zt|num[i+1].sd],f[zt]); for(int zt=0;zt<li;zt++) if(f[zt]) { for(int ut=zt;ut;ut=(ut-1)&zt)d[ut]=ad(d[ut],f[zt]); d[0]=ad(d[0],f[zt]); } } int main() { scanf("%d%d%d",&n,&G,&L); if(L%G==0) { divi(); getnum(1,1,0); sort(num+1,num+len+1); for(int i=1;i<=len;i++)leg[num[i].ft]=i; DP(); } int Q,x; scanf("%d",&Q); int li=(1<<plen*2)-1; while(Q--) { scanf("%d",&x); if(L%G!=0){puts("0");continue;} if(!leg.count(x)){puts("0");} else { int D=d[li^num[leg[x]].sd]; printf("%d\n",ad((LL)re(f[li],D)*(mod/2+1)%mod,D)); } } return 0; }
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #include<map> #define ft first #define sd second using namespace std; const int _=1e2; const int taxp=8; const int maxp=taxp+2; const int maxz=(1<<2*taxp)+_; const int maxn=8e2+10; typedef long long LL; typedef pair<int,int> pp; const int mod=1e9+7; inline int ad(int x,int y){return (x>=mod-y)?x-mod+y:x+y;} inline int re(int x,int y){return (x<y)?x-y+mod:x-y;}; int n,G,L; int plen,p[maxp],q[2*maxp]; void divi() { plen=0; int K=L; for(int i=2;i*i<=K;i++) if(K%i==0){p[++plen]=i;while(K%i==0)K/=i;} if(K!=1)p[++plen]=K; sort(p+1,p+plen+1); K=L; for(int i=1;i<=plen;i++) if(K%p[i]==0){while(K%p[i]==0)K/=p[i],q[i+plen]++;} K=G; for(int i=1;i<=plen;i++) if(K%p[i]==0){while(K%p[i]==0)K/=p[i],q[i]++;} } int len;pp num[maxn]; map<int,int>leg; void getnum(int k,int d,int s) { if(k==plen+1){num[++len]=make_pair(d,s);return ;} for(int i=q[k],g=int(pow(p[k],q[k])); i<=q[plen+k]; i++,g*=p[k]) if((LL)d*g<=n)getnum(k+1,d*g, s | ((i==q[k])?(1<<k-1):0) | ((i==q[plen+k])?(1<<plen+k-1):0) ); } int li,f[30][maxz],sum,as[maxn]; void flu(int *a,int i) { for(int zt=li;zt>=0;zt--) if(a[zt])a[zt|num[i].sd]=ad(a[zt|num[i].sd],a[zt]); } void dc(int k,int l,int r) { if(l==r) { if(l==len) { memcpy(f[k],f[k-1],sizeof(f[k])); flu(f[k],l); sum=f[k][li]; } as[l]=re(sum,f[k-1][li]); return ; } int mid=(l+r)/2; memcpy(f[k],f[k-1],sizeof(f[k])); for(int i=l;i<=mid;i++)flu(f[k],i); dc(k+1,mid+1,r); memcpy(f[k],f[k-1],sizeof(f[k])); for(int i=mid+1;i<=r;i++)flu(f[k],i); dc(k+1,l,mid); } int main() { scanf("%d%d%d",&n,&G,&L); if(L%G==0) { divi(); getnum(1,1,0); sort(num+1,num+len+1); for(int i=1;i<=len;i++)leg[num[i].ft]=i; li=(1<<plen*2)-1; f[0][0]=1;dc(1,1,len); } int Q,x; scanf("%d",&Q); while(Q--) { scanf("%d",&x); if(L%G!=0){puts("0");continue;} if(!leg.count(x)){puts("0");} else printf("%d\n",as[leg[x]]); } return 0; }
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #include<map> #define ft first #define sd second using namespace std; const int _=1e2; const int taxp=8; const int maxp=taxp+2; const int maxz=(1<<2*taxp)+_; const int maxn=8e2+10; typedef long long LL; typedef pair<int,int> pp; const int mod=1e9+7; inline int ad(int x,int y){return (x>=mod-y)?x-mod+y:x+y;} inline int re(int x,int y){return (x<y)?x-y+mod:x-y;}; int n,G,L; int plen,p[maxp],q[2*maxp]; void divi() { plen=0; int K=L; for(int i=2;i*i<=K;i++) if(K%i==0){p[++plen]=i;while(K%i==0)K/=i;} if(K!=1)p[++plen]=K; sort(p+1,p+plen+1); K=L; for(int i=1;i<=plen;i++) if(K%p[i]==0){while(K%p[i]==0)K/=p[i],q[i+plen]++;} K=G; for(int i=1;i<=plen;i++) if(K%p[i]==0){while(K%p[i]==0)K/=p[i],q[i]++;} } int len;pp num[maxn]; map<int,int>leg; void getnum(int k,int d,int s) { if(k==plen+1) { num[++len]=make_pair(d,s); // printf("%d %d\n",d,s); return ; } for(int i=q[k],g=int(pow(p[k],q[k])); i<=q[plen+k]; i++,g*=p[k]) if((LL)d*g<=n)getnum(k+1,d*g, s | ((i==q[k])?(1<<k-1):0) | ((i==q[plen+k])?(1<<plen+k-1):0) ); } void FWT(int *a,int n,int op) { for(int i=1;i<n;i<<=1) for(int j=0;j<n;j+=(i<<1)) for(int k=0;k<i;k++) if(op==1)a[j+k+i]=ad(a[j+k+i],a[j+k]); else a[j+k+i]=re(a[j+k+i],a[j+k]); } int f[maxn][maxz],g[maxn][maxz],h[maxz],d[maxn]; void DP() { f[0][0]=1; int li=(1<<plen*2)-1; for(int i=0;i<len;i++) for(int j=0;j<=li;j++) if(f[i][j]) { f[i+1][j|num[i+1].sd]=ad(f[i+1][j|num[i+1].sd],f[i][j]); f[i+1][j]=ad(f[i+1][j],f[i][j]); } g[len+1][0]=1; for(int i=len+1;i>1;i--) for(int j=0;j<=li;j++) if(g[i][j]) { g[i-1][j|num[i-1].sd]=ad(g[i-1][j|num[i-1].sd],g[i][j]); g[i-1][j]=ad(g[i-1][j],g[i][j]); } d[1]=g[2][li],d[len]=f[len-1][li]; for(int i=2;i<len;i++) { FWT(f[i-1],li+1,1),FWT(g[i+1],li+1,1); for(int j=0;j<=li;j++)h[j]=(LL)f[i-1][j]*g[i+1][j]%mod; FWT(h,li+1,-1); d[i]=h[li]; } } int main() { scanf("%d%d%d",&n,&G,&L); if(L%G==0) { divi(); getnum(1,1,0); sort(num+1,num+len+1); for(int i=1;i<=len;i++)leg[num[i].ft]=i; DP(); } int Q,x; scanf("%d",&Q); int li=(1<<plen*2)-1; while(Q--) { scanf("%d",&x); if(L%G!=0){puts("0");continue;} if(!leg.count(x)){puts("0");} else printf("%d\n",re(f[len][li],d[leg[x]])); } return 0; }