1. 程式人生 > 實用技巧 >Luogu P2150 [NOI2015]壽司晚宴

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;
}