提议
有n-1个数,分别是2到n。现在有两个人,每个人可以选择其中的若干个数,每个数至多被一个人选择。问有多少种选择方案满足不存在两个不互质的数分别位于不同的人手中。取模。
n≤500
n
≤
500
分析
我们把所有素数分成不大于
n−−√
n
的和大于
n−−√
n
的两部分。
对于大于
n−−√
n
的素数,一个数中最多只存在一个,所以我们可以把所有数按照其拥有的第二部分素因数来分类,对于没有大于
n−−√
n
素因子的数则每个数单独作为一类。
注意到不大于
n−−√
n
的素数数量不超过8,那么可以考虑状压一波。
设
fi,s1,s2
f
i
,
s
1
,
s
2
表示对于前
i
i
类,第一个人手中的数的第一类素因子并集为,第二个人的是
s2
s
2
的方案。
类与类之间显然是互不干扰的,并且同一类的数不能被两个人同时选,那么对于某一类,我们可以预处理出
gi,s
g
i
,
s
表示第
i
i
类,选出若干个数,其第一类素因子并集为的方案。
那么我们只要枚举
s
s
,就可以完成对的转移。注意这里可以通过枚举子集的方式来枚举
s
s
<script type="math/tex" id="MathJax-Element-1387">s</script>。
复杂度的话,大概算一算发现还是能过的,而且一般跑不满。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
typedef long long LL;
const int N=505;
const int M=305;
int n,m,MOD,B,prime[N],pri,tot,a[N][N],f[2][M][M],g[N][M],tmp[M],bin[10];
bool vis[N],not_prime[N];
void get_prime(int n)
{
for (int i=2;i<=n;i++)
{
if (!not_prime[i]) prime[++pri]=i;
for (int j=1;j<=pri&&i*prime[j]<=n;j++)
{
not_prime[i*prime[j]]=1;
if (i%prime[j]==0) break;
}
}
}
void pre_dp()
{
for (int i=1;i<=tot;i++)
{
g[i][0]=1;
for (int j=1;j<=a[i][0];j++)
{
int s=0;
for (int k=1;k<=m;k++) if (a[i][j]%prime[k]==0) s+=bin[k-1];
for (int k=0;k<bin[m];k++) tmp[k]=g[i][k];
for (int k=0;k<bin[m];k++)
if (g[i][k]) (tmp[k|s]+=g[i][k])%=MOD;
for (int k=0;k<bin[m];k++) g[i][k]=tmp[k];
}
g[i][0]--;
}
}
int main()
{
scanf("%d%d",&n,&MOD);
get_prime(n);
B=sqrt(n);m=0;
while (m<pri&&prime[m+1]<=B) m++;
bin[0]=1;
for (int i=1;i<=m;i++) bin[i]=bin[i-1]*2;
for (int i=m+1;i<=pri;i++)
{
tot++;
for (int j=prime[i];j<=n;j+=prime[i]) a[tot][++a[tot][0]]=j,vis[j]=1;
}
for (int i=2;i<=n;i++) if (!vis[i]) tot++,a[tot][++a[tot][0]]=i;
pre_dp();
f[0][0][0]=1;
int now=0;
for (int i=1;i<=tot;i++)
{
for (int s1=0;s1<bin[m];s1++)
for (int s2=0;s2<bin[m];s2++)
f[now^1][s1][s2]=f[now][s1][s2];
for (int s1=0;s1<bin[m];s1++)
for (int s2=0;s2<bin[m];s2++)
{
if (!f[now][s1][s2]) continue;
int w=s1^(bin[m]-1);
for (int s3=w;s3>=0;s3=!s3?s3-1:((s3-1)&w))
if (g[i][s3]) (f[now^1][s1][s2|s3]+=(LL)f[now][s1][s2]*g[i][s3]%MOD)%=MOD;
w=s2^(bin[m]-1);
for (int s3=w;s3>=0;s3=!s3?s3-1:((s3-1)&w))
if (g[i][s3]) (f[now^1][s1|s3][s2]+=(LL)f[now][s1][s2]*g[i][s3]%MOD)%=MOD;
}
now^=1;
}
int ans=0;
for (int s1=0;s1<bin[m];s1++)
for (int s2=0;s2<bin[m];s2++)
(ans+=f[now][s1][s2])%=MOD;
printf("%d",ans);
return 0;
}