洛谷2150 BZOJ1497 NOI2015 寿司晚宴 状压dp 数论

版权声明:本文为博主原创文章,可以转载但是必须声明版权。 https://blog.csdn.net/forever_shi/article/details/88545194

题目链接

题意:
你有数值为 2   n 2~n n 1 n-1 个数,你要把这些数分给两个人,可以有点数不分给任何人,每个人分到的数可以为空。分的时候如果第一个人分到的数存在一个数 x x ,第二个人分到的数存在一个数 y y ,使得 x x y y 与共同的质因子,那么就不合法。也就是对于某一个质因子,它的所有倍数只可能分给同一个人。求有多少种合法的分配数的方案。 n < = 500 n<=500 < = 1 e 9 模数<=1e9

题解:
这个题在前两天学校的模拟赛中做过一个类似的题,当时也自己想出一个思路,当时没想好细节,就没写出来,但是那个题感觉比这个题要麻烦,于是这个题的思路就比较容易想了。

首先我们考虑数据范围小的情况,对于 n < = 30 n<=30 的情况,我们可以直接把质因子状压一下,对于每个数,我们看一下它含哪些质因子,然后就用一个状压dp来算就可以了。我们设 d p [ i ] [ j ] dp[i][j] 表示第一个人选了 i i 这个集合的质因子,第二个人选了 j j 这个集合的质因子的方案数,然后转移就是 d p [ i s [ x ] ] [ j ] + = d p [ i ] [ j ] ( j & s [ x ] = 0 ) , d p [ i ] [ j s [ x ] ] + = d p [ i ] [ j ] ( i & s [ x ] = 0 ) dp[i|s[x]][j]+=dp[i][j](j\& s[x]=0),dp[i][j|s[x]]+=dp[i][j](i\& s[x]=0) ,这里 s [ x ] s[x] 就表示 x x 这个数的所有质因子状压后的集合。

这个思路对于数据更大的情况的问题就是质因子太多,没法状压了。但是我们可以从另一个方面考虑,如果一个数含有一个大于 n \sqrt{n} 的质因子,那么这种质因子它最多含有一个,剩下的只可能还有一些小于 n \sqrt{n} 的质因子。于是我们考虑状压这些小于 n \sqrt{n} 的质因子,并对大的质因子特别处理一下。

我们的思路是,把含同一个大于 n \sqrt{n} 的质因子的数放在一起计算,这些数要么全都分给第一个人,要么全都分给第二个人。那么我们在新出现一个之前没算过的大于 n \sqrt{n} 的质因子的时候我们设 f [ i ] [ j ] f[i][j] 表示把含有这个质因子的数全分给第一个人,第一个人的集合是 i i ,第二个人的集合是 j j 的方案数, g [ i ] [ j ] g[i][j] 表示把含有这个质因子的数全分给第二个人,第一个人的集合是 i i ,第二个人的集合是 j j 的方案数,这两个数组的初始值都是 d p [ i ] [ j ] dp[i][j] 的值。然后发现这个质因子结束的时候再更新 d p [ i ] [ j ] dp[i][j] 。此时的 d p [ i ] [ j ] = f [ i ] [ j ] + g [ i ] [ j ] d p [ i ] [ j ] dp[i][j]=f[i][j]+g[i][j]-dp[i][j] ,原因是含这个大于 n \sqrt{n} 的质因子的所有数既没有给第一个人也没有给第二个人的方案数在 f f g g 中各被算了一遍,重复计算了,所以我们要减去一遍,而应该减去的正好就是原来的这个没考虑这个大质因子时的 d p [ i ] [ j ] dp[i][j] 。剩下的就是简单状压dp处理了。最后答案就是把所有的KaTeX parse error: Expected 'EOF', got '&' at position 11: dp[i][j](i&̲j=0)的情况加起来。

这样的复杂度是 O ( n 2 8 + 8 ) O(n*2^{8+8}) ,复杂度是 3 e 7 3e7 量级的,可以通过这个题。

代码:

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

int n,p[10],cnt=8,b[510],dp[1025][1025],f[1025][1025],g[1025][1025],mod,ans;
struct node
{
	int s,val;
}a[1000];
inline int cmp(node x,node y)
{
	return x.val<y.val;
}
int main()
{
	scanf("%d%d",&n,&mod);
	p[1]=2;
	p[2]=3;
	p[3]=5;
	p[4]=7;
	p[5]=11;
	p[6]=13;
	p[7]=17;
	p[8]=19;
	for(int i=1;i<=n-1;++i)
	{
		b[i]=i+1;
		for(int j=1;j<=cnt;++j)
		{
			if(b[i]%p[j]==0)
			{
				a[i].s|=(1<<(j-1));
				while(b[i]%p[j]==0)
				b[i]/=p[j];
			}
		}
		a[i].val=b[i];
	}
	sort(a+1,a+n,cmp);
	dp[0][0]=1;
	int mx=(1<<8)-1;
	for(int i=1;i<=n-1;++i)
	{
		if(a[i].val==1||a[i].val!=a[i-1].val)
		{
			memcpy(f,dp,sizeof(dp));
			memcpy(g,dp,sizeof(dp));
		}
		for(int j=mx;j>=0;--j)
		{
			for(int k=mx;k>=0;--k)
			{
				if(j&k)
				continue;
				if((a[i].s&k)==0)
				f[j|a[i].s][k]=(f[j|a[i].s][k]+f[j][k])%mod;
				if((a[i].s&j)==0)
				g[j][k|a[i].s]=(g[j][k|a[i].s]+g[j][k])%mod;
			}
		}
		if(a[i].val==1||a[i].val!=a[i+1].val||i==n-1)
		{
			for(int j=mx;j>=0;--j)
			{
				for(int k=mx;k>=0;--k)
				{
					if(j&k)
					continue;
					dp[j][k]=((f[j][k]+g[j][k])%mod-dp[j][k]+mod)%mod;
				}
			}
		}
	}
	for(int i=0;i<=mx;++i)
	{
		for(int j=0;j<=mx;++j)
		{
			if(i&j)
			continue;
			ans=(ans+dp[i][j])%mod;
		}	
	}
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/88545194
今日推荐