uoj129. [NOI2015] 寿司晚宴
题意:
有 $n-1$ 种寿司和两个人,每种寿司只有一个,且编号为 $[2,n]$ 每个人可以选择一些寿司(不一定要选完),求有多少种选择的方案使得一个人手上的任何一个寿司都与另一个人手上的任何一个寿司的编号互质。
$n<=500,p<=10^9$
题解:
由数据范围可以得知,n并不大,由于有不互质的限制,所以考虑质因数。容易发现 $\sqrt{n}<23$,小于 $23$ 的质数只有 $8$ 个,可以直接暴力 $2^8*2^8$ 存状态并转移。但是问题在于,不只有这几个质因数,还有可能有比 $\sqrt{n}$ 大的质因数。不过这种质因数最多只有一个,我们可以将 $2$~$n$ 的数字按照比 $\sqrt{n}$ 大的质因数的大小排序。对于每一组比 $\sqrt{n}$ 大的质因数相等的寿司,这一组只有一个人能选择,可以进行 $dp$,计算方案数并合并。
#include<cstdio> #include<algorithm> #include<cstdlib> #include<cmath> using namespace std; const int prime[]={2,3,5,7,11,13,17,19},h=1<<8; int n; long long mod,f[h][h][3]; typedef struct{ int a,t; }P; bool cmp(P aa,P bb){ return (aa.a<bb.a); } P p[502]; int main() { scanf("%d%lld",&n,&mod); for (int i=0;i<n-1;i++) { p[i].a=i+2; for (int j=0;j<8;j++) while(p[i].a%prime[j]==0) { p[i].a/=prime[j]; p[i].t|=(1<<j); } } sort(p,p+n-1,cmp); f[0][0][0]=1; for (int i=0;i<n-1;i++) { if (!i || p[i].a!=p[i-1].a || p[i].a==1) for (int j=0;j<h;j++) for (int k=0;k<h;k++) f[j][k][1]=f[j][k][2]=f[j][k][0]; for (int j=h-1;j>=0;j--) for (int k=h-1;k>=0;k--) { if (!(k&p[i].t))f[j|p[i].t][k][1]=(f[j|p[i].t][k][1]+f[j][k][1])%mod; if (!(j&p[i].t))f[j][k|p[i].t][2]=(f[j][k|p[i].t][2]+f[j][k][2])%mod; } if (i==n-2 || p[i].a!=p[i+1].a || p[i].a==1) for (int j=0;j<h;j++) for (int k=0;k<h;k++) f[j][k][0]=((f[j][k][1]+f[j][k][2]-f[j][k][0])%mod+mod)%mod; } long long ans=0; for (int i=0;i<h;i++) for (int j=0;j<h;j++) if ((i&j)==0)ans=(ans+f[i][j][0])%mod; printf("%lld",ans); return 0; }