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;
}
辣鸡老年选手AFO在即