LOJ#2131. 「NOI2015」寿司晚宴
$n \leq 500$,$2-n$这些数字,两个人挑,可以重复挑,问有几种方案中,一个人选的所有数字与另一个人选的所有数字都互质。
不像前两题那么抠脚。。
如果$n$比较小的话,可以把两个人选的数字对应的质因子状压一下,$f(i,j,k)$--前$i$个数,第一个人选状态$j$,第二个人选状态$k$,状态表示质因子。
质因子的根号相关性质:根号n之后的每个质因子最多只会在一个数里出现一次。也就是说,对根号n前面的质因子我们是可能一次选若干种的,但根号n后面的每个质因子每次只能选一种,所以可以单独枚举,再用根号n以前的状态表示。$g(i,j,k)$--前$i$个含质数$p$的数,只让第一个人选,第二个人保持状态$k$的方案数;$h(i,j,k)$--前$i$个含质数$p$的数,只让第二个人选,第一个人保持状态$j$的方案数。如此dp完,$ans(j,k)$表示最后的$f(i,j,k)$,那么$ans(j,k)=g(t,j,k)+h(t,j,k)-ans(j,k)$,其中$t$表示含质因数$p$的数的个数,因为两个人都不选的方案算了两次。
1 //#include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 //#include<math.h> 5 //#include<set> 6 //#include<queue> 7 //#include<bitset> 8 //#include<vector> 9 #include<algorithm> 10 #include<stdlib.h> 11 using namespace std; 12 13 #define LL long long 14 int qread() 15 { 16 char c; int s=0,f=1; while ((c=getchar())<'0' || c>'9') (c=='-') && (f=-1); 17 do s=s*10+c-'0'; while ((c=getchar())>='0' && c<='9'); return s*f; 18 } 19 20 //Pay attention to '-' , LL and double of qread!!!! 21 22 int n,mod; 23 #define maxn 555 24 int f[2][maxn][maxn],g[2][maxn][maxn],h[2][maxn][maxn],cur=0; 25 26 int prime[maxn],lp,xx[maxn],ss[maxn]; bool notprime[maxn]; 27 void makeprime() 28 { 29 ss[2]=0; ss[3]=1; ss[5]=2; ss[7]=3; 30 ss[11]=4; ss[13]=5; ss[17]=6; ss[19]=7; 31 for (int i=2;i<=n;i++) 32 { 33 if (!notprime[i]) {prime[++lp]=i; xx[i]=i;} 34 for (int j=1,tmp;j<=lp && 1ll*i*prime[j]<=n;j++) 35 { 36 notprime[tmp=i*prime[j]]=1; 37 xx[tmp]=prime[j]; 38 if (!(i%prime[j])) break; 39 } 40 } 41 } 42 43 int num[maxn],len; int S,P; 44 void mm(int x) 45 { 46 len=0; 47 while (x>1) {num[++len]=xx[x]; x/=xx[x];} 48 sort(num+1,num+1+len); len=unique(num+1,num+1+len)-num-1; 49 S=0; for (int i=1;i<=len;i++) if (num[i]<=19) S|=1<<ss[num[i]]; 50 if (num[len]>19) P=num[len]; else P=0; 51 } 52 53 int list[maxn][maxn]; 54 void play(int &x,int v) {x+=v; x-=x>=mod?mod:0;} 55 int main() 56 { 57 n=qread(); mod=qread(); 58 makeprime(); 59 for (int i=2;i<=n;i++) {mm(i); list[P][++list[P][0]]=S;} 60 61 int ts=1<<8; f[0][0][0]=1; cur=0; 62 for (int i=1;i<=list[0][0];i++) 63 { 64 int u=list[0][i]; 65 memset(f[cur^1],0,sizeof(f[cur^1])); 66 for (int j=0;j<ts;j++) 67 for (int k=0;k<ts;k++) if (f[cur][j][k]) 68 { 69 play(f[cur^1][j][k],f[cur][j][k]); 70 int nj=j|u; if ((nj&k)==0) play(f[cur^1][nj][k],f[cur][j][k]); 71 int nk=k|u; if ((j&nk)==0) play(f[cur^1][j][nk],f[cur][j][k]); 72 } 73 cur^=1; 74 } 75 76 for (int j=0;j<ts;j++) 77 for (int k=0;k<ts;k++) 78 f[0][j][k]=f[cur][j][k]; 79 for (int i=2;i<=n;i++) if (!notprime[i]) 80 { 81 cur=0; 82 for (int j=0;j<ts;j++) 83 for (int k=0;k<ts;k++) 84 g[0][j][k]=f[0][j][k],h[0][j][k]=f[0][j][k]; 85 for (int t=1;t<=list[i][0];t++) 86 { 87 int u=list[i][t]; 88 memset(g[cur^1],0,sizeof(g[cur^1])); 89 memset(h[cur^1],0,sizeof(h[cur^1])); 90 for (int j=0;j<ts;j++) 91 for (int k=0;k<ts;k++) if (g[cur][j][k]) 92 { 93 play(g[cur^1][j][k],g[cur][j][k]); 94 int nj=j|u; if ((nj&k)==0) play(g[cur^1][nj][k],g[cur][j][k]); 95 } 96 for (int j=0;j<ts;j++) 97 for (int k=0;k<ts;k++) if (h[cur][j][k]) 98 { 99 play(h[cur^1][j][k],h[cur][j][k]); 100 int nk=k|u; if ((j&nk)==0) play(h[cur^1][j][nk],h[cur][j][k]); 101 } 102 cur^=1; 103 } 104 for (int j=0;j<ts;j++) 105 for (int k=0;k<ts;k++) 106 f[0][j][k]=((g[cur][j][k]+h[cur][j][k]-f[0][j][k])%mod+mod)%mod; 107 } 108 109 int ans=0; 110 for (int j=0;j<ts;j++) 111 for (int k=0;k<ts;k++) 112 play(ans,f[0][j][k]); 113 printf("%d\n",ans); 114 return 0; 115 }