Luogu P2150 [NOI2015]寿司晚宴

之前各种上课已经听了好多次了,最近就给它写掉了

首先考虑一个很naive的DP,首先我们发现题意就是每个质因子只能存在于一个集合中

所以直接把每个质因子是否出现压入状态,设(f_{i,j})表示两个集合中分别是否存在某种质因子

然后因为(500)内的质数挺多的,直接就GG了

但是我们考虑到有关因数的常用结论,每个数的质因数最多只有一个大于等于(sqrt n)

然后我们一算,小于(sqrt {500})的质数只有(8)个,可以用上面的DP来设状态

然后就涉及到关于大于(sqrt n)的质因子了,显然我们可以每次枚举它们

因为它们只能放在一个集合中,因此我们直接设(g1_{i,j},g2_{i,j})表示把这些数放到第一个集合和第二个集合中的方案数

显然(g1,g2)的转移可以从(f)推来,最后注意一下空集的情况算重了要减去

#include<cstdio>
#include<iostream>
#include<algorithm>
#define RI register int
#define CI const int&
#define int long long
using namespace std;
const int N=505,M=1<<8;
struct data
{
	int p,s; // p: greater than sqrt(n)'s prime; s: state of less than sqrt(n)'s prime
	friend inline bool operator < (const data& A,const data& B)
	{
		return A.p<B.p;
	}
}a[N]; int n,mod,f[M][M],g1[N][M],g2[N][M],prime[N],tot,ans;
inline bool isprime(CI n)
{
	for (RI i=2;i*i<=n;++i) if (n%i==0) return 0; return 1;
}
inline void inc(int& x,CI y)
{
	if ((x+=y)>=mod) x-=mod;
}
signed main()
{
	RI i,j,k,x,y; for (scanf("%lld%lld",&n,&mod),i=2;i<=n;++i)
	if (isprime(i)) prime[++tot]=i; for (i=2;i<=n;++i)
	{
		for (j=1;j<=min(tot,8LL);++j) if (i%prime[j]==0) a[i].s|=(1<<j-1);
		for (;j<=tot;++j) if (i%prime[j]==0) a[i].p=prime[j];
	}
	for (sort(a+2,a+n+1),f[0][0]=1,i=2;i<=n&&!a[i].p;++i)
	for (x=M-1;~x;--x) for (y=M-1;~y;--y) if (f[x][y])
	{
		if (!(a[i].s&x)) inc(f[x][y|a[i].s],f[x][y]);
		if (!(a[i].s&y)) inc(f[x|a[i].s][y],f[x][y]);
	}
	for (;i<=n;i=j+1)
	{
		for (x=M-1;~x;--x) for (y=M-1;~y;--y) g1[x][y]=g2[x][y]=f[x][y];
		for (j=i;j<n&&a[j+1].p==a[j].p;++j); for (k=i;k<=j;++k)
		for (x=M-1;~x;--x) for (y=M-1;~y;--y)
		{
			if (!(a[k].s&x)) inc(g2[x][y|a[k].s],g2[x][y]);
			if (!(a[k].s&y)) inc(g1[x|a[k].s][y],g1[x][y]);
		}
		for (x=M-1;~x;--x) for (y=M-1;~y;--y) f[x][y]=(g1[x][y]+g2[x][y]-f[x][y]+mod)%mod;
	}
	for (x=M-1;~x;--x) for (y=M-1;~y;--y) inc(ans,f[x][y]);
	return printf("%lld",ans),0;
}
原文地址:https://www.cnblogs.com/cjjsb/p/13337518.html