BZOJ 4197: [Noi2015]寿司晚宴
状压<=sqrt(n)的所有质因数
对所有数分解质因数,大于sqrt(n)的质因数最多出现一次
包含相同大于sqrt(n)的质因数的数应该都归属于同一个人
按大于sqrt(n)的质因数排序
F[X][Y]表示两个人小于sqrt(n)的质因数的选取情况
G[0/1][X][Y]用于转移包含相同的大于sqrt(n)的质因子的数
精妙之处在于用状压然后分类避免了质因数之间相互影响的情况
#include<cstdio> #include<algorithm> using namespace std; int n,mod,F[305][305],G[2][305][305]; int prime[8]={2,3,5,7,11,13,17,19}; struct node{ int S,val; }E[505]; bool cmp(node a,node b){ return a.val<b.val; } int main(){ scanf("%d%d",&n,&mod); for (int i=2; i<=n; i++){ int x=i; for (int j=0; j<8; j++) if (x%prime[j]==0){ E[i-1].S|=1<<j; while (x%prime[j]==0) x/=prime[j]; } E[i-1].val=x; } n--; sort(E+1,E+n+1,cmp); F[0][0]=1; int N=8; for (int i=1; i<=n; i++){ if (E[i].val==1 || E[i].val!=E[i-1].val){ for (int X=0; X<(1<<N); X++) for (int Y=0; Y<(1<<N); Y++) G[0][X][Y]=G[1][X][Y]=F[X][Y]; } for (int X=(1<<N)-1; X>=0; X--) for (int Y=(1<<N)-1; Y>=0; Y--) if (!(X&Y)){ if (!(E[i].S&Y)) (G[0][X|E[i].S][Y]+=G[0][X][Y])%=mod; if (!(E[i].S&X)) (G[1][X][Y|E[i].S]+=G[1][X][Y])%=mod; } if (E[i].val==1 || E[i].val!=E[i+1].val){ for (int X=0; X<(1<<N); X++) for (int Y=0; Y<(1<<N); Y++) if (!(X&Y)) F[X][Y]=((G[0][X][Y]+G[1][X][Y]-F[X][Y])%mod+mod)%mod; } } int ans=0; for (int X=0; X<(1<<N); X++) for (int Y=0; Y<(1<<N); Y++) if (!(X&Y)) (ans+=F[X][Y])%=mod; printf("%d\n",ans); return 0; }