1. 程式人生 > >【BZOJ4197】[Noi2015]壽司晚宴 狀壓DP+分解質因數

【BZOJ4197】[Noi2015]壽司晚宴 狀壓DP+分解質因數

pre () mod 統計 ring 發現 spa 情況 編號

【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 10000

Sample Output

9

HINT

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+分解質因數