链接
https://www.luogu.org/problem/show?pid=2150
题解
考虑暴力,当
n≤30
时,素数有2 3 5 7 11 13 17 19 23 29,一共10个,状压dp可行,f[i][j]表示每个人选数的状态,然后暴力转移就好了,复杂度
O(22N)
。然后我就想不出来了……
有一个常用到的性质就是,对于任何一个数字
x
,其大于
可以只压这8个素数,
f[i][j]
表示第一个人的选数状态为i,第二个人为j的方案数。
将
[2,500]
的所有数分解质因数,大于19的素数最多有一个,记下来,记作gp[i](giant prime),小素数们压进二进制数,记作tp[i](tiny primes)。
接下来是转移。
对于gp相等的数,我们划分到一组里去单独dp,因为这一组中只能让一个人选数,一个人选了另一个人就不能再这组里选了。
对于一个组,其giant prime是相同的,令
g[i][j]
表示已经考虑了组中的所有数和前面做完的组的所有数,强制让第一个人选择这个组中的数(一个数不选的情况也纳入考虑),第二个人不能选,这样的方案数。
初始状态
g[i][j]=f[i][j]
那么我们每次加入组中的一个数
x
,令
做完一组之后,要将
g
和
这样有BUG,没有大素数的那一组不能这样转移,这样转移方案数会变少,因为这一组中只要两个人不选相同的数字就行,和前面不一样。
这时直接
f[i bitor k]+=f[i][j]
,
f[i][j bitor k]+=f[i][j]
答案就是所有状态加起来。
代码
//状压DP
#include <cstdio>
#include <algorithm>
#define maxn 1000
#define ll long long
using namespace std;
ll N, P, f[maxn][maxn], g[maxn][maxn], tp[maxn], hp[maxn], mark[maxn], prime[maxn],
num[maxn], h[maxn][maxn];
bool cmp(ll a, ll b){return hp[a]<hp[b];}
void init()
{
ll i, j;
scanf("%lld%lld",&N,&P);
for(i=2;i<=N;i++)
{
if(!mark[i])prime[++prime[0]]=i;
for(j=1;j<=prime[0] and i*prime[j]<=N;j++)
{
mark[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}
for(i=2;i<=N;i++)
{
for(j=1;prime[j]*prime[j]<=N;j++)if(i%prime[j]==0)tp[i]|=(1<<j-1);
for(;prime[j]<=i and j<=prime[0];j++)if(i%prime[j]==0)hp[i]=prime[j];
}
for(i=2;i<=N;i++)num[i]=i;
sort(num+2,num+N+1,cmp);
}
void dp()
{
ll l, r, p, i, j, k, ans=0;
f[0][0]=1;
for(l=2;l<=N;l=r+1)
{
for(r=l;hp[num[r]]==hp[num[l]] and r<=N;r++);r--;
if(hp[num[l]]!=0)
{
for(i=0;i<256;i++)for(j=0;j<256;j++)g[i][j]=f[i][j];
for(p=l;p<=r;p++)
{
k=tp[num[p]];
for(i=255;i>=0;i--)
for(j=255;j>=0;j--)
{
if((i&j)or(j&k))continue;
g[i|k][j]+=g[i][j];
}
}
for(i=0;i<256;i++)for(j=0;j<256;j++)f[i][j]=(g[i][j]+g[j][i]-f[i][j])%P;
}
else
{
for(p=l;p<=r;p++)
{
k=tp[num[p]];
for(i=255;i>=0;i--)
for(j=255;j>=0;j--)
{
if(i&j)continue;
f[i][j]%=P;
if((j&k)==0)f[i|k][j]+=f[i][j];
if((i&k)==0)f[i][j|k]+=f[i][j];
}
}
}
}
for(i=0;i<256;i++)for(j=0;j<256;j++)ans=(ans+f[i][j])%P;
printf("%lld",((ans%P)+P)%P);
}
int main()
{
init();
dp();
return 0;
}