洛谷题目传送门
解题思路
首先考虑30pts的做法
因为两人选的数必须互质,因此可以想到只要维护二者没有共同质因子就行了
因为质因子个数很少,考虑状压DP
设 dp[S][T] 表示 A选了数的质因子状态为 S,B的质因子状态为 T
那么 dp[S|k][T]+=dp[S][T] ((k&T)==0)
dp[S][T|k]+=dp[S][T] ((k&S)==0)
其中 k 表示 第 i 个数 的质因子状态,因为可以用滚动数组优化,因此不需要存储 i
代码也很好写
然后是100pts的做法
有一个性质是 数 n 只有最多一个 大于 根号n 的质因子(记为大质因子)
因此如果我们计算出每个数的大质因子,并将其排序
这样大质因子相同的数就在一块了,在每一块里,要么只能让A选,要么只能让 B选,要么都不选
因此我们仿照30pts的做法分别维护让A选和让B选的方案数,再合并起来就是答案
具体来说 设 f1[S|k][T]+=f1[S][T]
f2[S|k][T]+=f2[S][T]
然后每一个块算完之后
dp[S][T]=f1[S][T]+f2[S][T]-dp[S][T]
因为不选的方案数会记两次,因此需要减掉
复杂度
代码如下
PS:这是我通过的第一道DP黑题,纪念一下
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
struct node
{
int v;
int big;
int S;
}num[510];
LL dp[260][260],f1[260][260],f2[260][260];
int p[10]={0,2,3,5,7,11,13,17,19};
int n,mod;
bool cmp(node x,node y)
{
return x.big<y.big;
}
void resolve(int x) //求大质因子
{
int tmp=num[x].v;
num[x].big=-1;
for(int i=1;i<=8;i++)
{
if(tmp%p[i]!=0) continue;
num[x].S|=(1<<(i-1));
while(tmp%p[i]==0) tmp/=p[i];
}
if(tmp!=1) num[x].big=tmp;
}
inline int read()
{
int X=0; bool flag=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
if(flag) return X;
return ~(X-1);
}
int main()
{
freopen("dinner.in","r",stdin);
freopen("dinner.out","w",stdout);
n=read();
mod=read();
for(int i=2;i<=n;i++)
{
num[i-1].v=i;
resolve(i-1);
}
sort(num+1,num+n,cmp);
dp[0][0]=1;
for(int i=1;i<n;i++)
{
if(i==1||num[i-1].big!=num[i].big||num[i].big==-1) //首先需要先把dp的值赋值给f1,f2
{
memcpy(f1,dp,sizeof(f1));
memcpy(f2,dp,sizeof(f2));
}
for(int j=255;j>=0;j--) //滚动数组优化,需要倒叙枚举
{
for(int k=255;k>=0;k--)
{
if(j&k) continue;
if((num[i].S&k)==0) f1[j|num[i].S][k]+=f1[j][k];
if((num[i].S&j)==0) f2[j][k|num[i].S]+=f2[j][k];
f1[j][k]=(f1[j][k]>=mod)?f1[j][k]%mod:f1[j][k];
f2[j][k]=(f2[j][k]>=mod)?f2[j][k]%mod:f2[j][k];
}
}
if(i==n-1||num[i].big!=num[i+1].big||num[i].big==-1)
{
for(int j=255;j>=0;j--)
{
for(int k=255;k>=0;k--)
{
if(j&k) continue;
dp[j][k]=((f1[j][k]+f2[j][k])%mod+mod-dp[j][k])%mod;
}
}
}
}
LL ans=0;
for(int i=0;i<=255;i++)
{
for(int j=0;j<=255;j++)
{
if((j&i)==0&&dp[i][j])
ans=(ans+dp[i][j])%mod;
}
}
cout<<ans;
return 0;
}