【BZOJ4197】[Noi2015]壽司晚宴 狀壓DP+分解質因數
【BZOJ4197】[Noi2015]壽司晚宴
Description
為了慶祝 NOI 的成功開幕,主辦方為大家準備了一場壽司晚宴。小 G 和小 W 作為參加 NOI 的選手,也被邀請參加了壽司晚宴。
在晚宴上,主辦方為大家提供了 n−1 種不同的壽司,編號 1,2,3,…,n−1,其中第 i 種壽司的美味度為 i+1 (即壽司的美味度為從 2 到 n)。 現在小 G 和小 W 希望每人選一些壽司種類來品嘗,他們規定一種品嘗方案為不和諧的當且僅當:小 G 品嘗的壽司種類中存在一種美味度為 x 的壽司,小 W 品嘗的壽司中存在一種美味度為 y 的壽司,而 x 與 y 不互質。 現在小 G 和小 W 希望統計一共有多少種和諧的品嘗壽司的方案(對給定的正整數 p 取模)。註意一個人可以不吃任何壽司。Input
輸入文件的第 1 行包含 2 個正整數 n,p,中間用單個空格隔開,表示共有 n 種壽司,最終和諧的方案數要對 p 取模。
Output
輸出一行包含 1 個整數,表示所求的方案模 p 的結果。
Sample Input
3 10000Sample Output
9HINT
2≤n≤500
0<p≤1000000000題解:我們考慮每個素數,它要麽在A中,要麽在B中,要麽都不在。我們定義<sqrt(500)(更具體的說是<=19,因為23*29>500)的質數為小質數,其余的為大質數,則2-n中的任意一個數都可以表示成1(或0)個大質數*若幹個小質數。因此我們可以狀壓小質數,枚舉大質數。
用g[0或1][x][y]表示當前的大質數在A或B中,A的小質數狀態為x,B的小質數狀態為y的方案數。我們預處理出對於每個大質數,可以和它搭配的 小質數組 有哪些(其實就是枚舉一個大質數p的所有倍數,將所有倍數都分解質因數)。如果當前這個數要被A選,相當於A既要選這個大質數也要選這個 小質數組 ,並且B既不能選這個大質數也不能選這個 小質數組 中的任一個質數。我們設這個 小質數組 的狀態為S,得到DP方程:
g[0][x|S][y]=g[0][x|S][y]+g[0][x][y]
g[1][x][y|S]=g[1][x][y|S]+g[1][x][y]
我們用f[x][y]統計答案,但是發現存在重復的情況。你可以理解為雖然這個數被A搶走了,使得B不能選這個數,但是A也不想選這個數。或者這個數被B搶走了,但是B也不想選。那麽重復的方案數是多少呢?就是之前的f[x][y]。所以新的f[x][y]=g[0][x][y]+g[1][x][y]-舊的f[x][y]。
同時,對於不包含大質數的數,要特殊處理。對於不同的大質數,用乘法原則統計答案(因為A可以把好多大質數都搶走);對於不同的小質數選擇方法,用加法原則統計答案(小質數不能既給A又給B);DP的時候要時刻滿足x&y==0。(以上都是只有本蒟蒻才不明白的地方。)
#include <cstdio> #include <cstring> #include <iostream> #include <vector> using namespace std; int n,mod,ans; int f[1<<8][1<<8],g[2][1<<8][1<<8]; int p2[510]; int pri[]={2,3,5,7,11,13,17,19}; vector<int> v[510]; int getpri(int x) { int i,ret=0; for(i=0;i<8;i++) { if(x%pri[i]==0) { ret|=(1<<i); while(x%pri[i]==0) x/=pri[i]; } } v[x].push_back(ret); } int main() { scanf("%d%d",&n,&mod); int i,j,x,y; for(i=2;i<=n;i++) getpri(i); f[0][0]=1; for(j=0;j<v[1].size();j++) { for(x=(1<<8)-1;~x;x--) { for(y=(1<<8)-1;~y;y--) if(!(x&y)) { if(!(y&v[1][j])) f[x|v[1][j]][y]=(f[x|v[1][j]][y]+f[x][y])%mod; if(!(x&v[1][j])) f[x][y|v[1][j]]=(f[x][y|v[1][j]]+f[x][y])%mod; } } } for(i=23;i<=n;i++) { if(!v[i].size()) continue; for(x=0;x<(1<<8);x++) for(y=0;y<(1<<8);y++) if(!(x&y)) g[0][x][y]=g[1][x][y]=f[x][y]; for(j=0;j<v[i].size();j++) { for(x=(1<<8)-1;~x;x--) { for(y=(1<<8)-1;~y;y--) if(!(x&y)) { if(!(y&v[i][j])) g[0][x|v[i][j]][y]=(g[0][x|v[i][j]][y]+g[0][x][y])%mod; if(!(x&v[i][j])) g[1][x][y|v[i][j]]=(g[1][x][y|v[i][j]]+g[1][x][y])%mod; } } } for(x=0;x<(1<<8);x++) for(y=0;y<(1<<8);y++) f[x][y]=((g[0][x][y]+g[1][x][y]-f[x][y])%mod+mod)%mod; } for(x=0;x<(1<<8);x++) for(y=0;y<(1<<8);y++) ans=(ans+f[x][y])%mod; printf("%d",ans); return 0; }
【BZOJ4197】[Noi2015]壽司晚宴 狀壓DP+分解質因數