【洛谷P2150】寿司晚宴

题目

题目链接:https://www.luogu.com.cn/problem/P2150
为了庆祝 NOI 的成功开幕,主办方为大家准备了一场寿司晚宴。小 G 和小 W 作为参加 NOI 的选手,也被邀请参加了寿司晚宴。
在晚宴上,主办方为大家提供了 (n-1) 种不同的寿司,编号 (1,2,3,ldots,n-1),其中第 (i) 种寿司的美味度为 (i+1)。(即寿司的美味度为从 (2)(n)
现在小 G 和小 W 希望每人选一些寿司种类来品尝,他们规定一种品尝方案为不和谐的当且仅当:小 G 品尝的寿司种类中存在一种美味度为 (x) 的寿司,小 W 品尝的寿司中存在一种美味度为 (y) 的寿司,而 (x)(y) 不互质。
现在小 G 和小 W 希望统计一共有多少种和谐的品尝寿司的方案(对给定的正整数 (p) 取模)。注意一个人可以不吃任何寿司。
(nleq 500,pleq 10^{10})

思路

(500) 以内一共有 (95) 个质数,不好状压。
我们发现这其中每一个数超过 (lfloorsqrt{500} floor=22) 的质因子只会有一个。我们设 (d_i) 为数字 (i) 超过 (22) 的质因子。然后把数字按照 (d_i) 排序。
显然这样对于每一段 (d_i) 相同的,这些数字只能由同一个人拿。
这样剩余的不超过 (22) 的质数就只有 (8) 个了。考虑状压。把每一段分别 dp。设 (f[0/1][i][j][k]) 表示选到第 (i) 个数,第一个人的质因子集合是 (j),第二个人的质因子集合是 (k) 时,这一段由第一个人还是第二个人拿的方案数。转移就枚举上一次的状态。预处理出每一个数的不超过 (22) 的质因子集合。空间可以以把 (i) 这一维滚动
注意当处理完一段之后需要合并。设 (g[i][j]) 表示处理完一段后,第一个人质因子集合为 (i),第二个人为 (j) 的方案数。然后与 (f) 合并就可以了。
时间复杂度 (O(2^{16}n))

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int prm[8]={2,3,5,7,11,13,17,19};
const int N=510,M=(1<<8);
int n,a[N],s[N],d[N];
ll ans,MOD,f[2][2][M][M],g[M][M];

bool cmp(int x,int y)
{
	return d[x]<d[y];
}

int main()
{
	scanf("%d",&n);
	scanf("%lld",&MOD);
	for (int i=2;i<=n;i++)
	{
		int p=i;
		for (int j=0;j<=7;j++)
			if (!(p%prm[j]))
			{
				s[i]|=(1<<j);
				while (!(p%prm[j])) p/=prm[j];
			}
		d[i]=p; a[i]=i;
	}
	sort(a+2,a+1+n,cmp);
	g[0][0]=1;
	for (int l=2;l<=n;l++)
	{
		int id=(l&1),i=a[l];
		if (d[i]!=d[a[l-1]] || d[i]==1)
		{
			for (int j=0;j<M;j++)
				for (int k=0;k<M;k++)
				{
					if (l!=2)
						g[j][k]=(f[0][id^1][j][k]+f[1][id^1][j][k]-g[j][k])%MOD;
					f[0][id^1][j][k]=f[1][id^1][j][k]=g[j][k];
				}
		}
		for (int j=0;j<M;j++)
			for (int k=0;k<M;k++)
			{
				f[0][id][j][k]=f[0][id^1][j][k];
				f[1][id][j][k]=f[1][id^1][j][k];
			}
		for (int j=0;j<M;j++)
			for (int k=0;k<M;k++)
			{
				if (!(k&s[i])) (f[0][id][j|s[i]][k]+=f[0][id^1][j][k])%=MOD;
				if (!(j&s[i])) (f[1][id][j][k|s[i]]+=f[1][id^1][j][k])%=MOD;
			}
	}
	int id=(n&1);
	for (int i=0;i<M;i++)
		for (int j=0;j<M;j++)
		{
			g[i][j]=(f[0][id][i][j]+f[1][id][i][j]-g[i][j])%MOD;
			ans=(ans+g[i][j])%MOD;
		}
	printf("%lld",(ans%MOD+MOD)%MOD);
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/14423019.html