因为每个数只有一个大于根号n的质因子,所以我们把每个数拆成一个大于根号n的质因子乘以一个数的形式,对于大于根号n的质因子相同的数,我们放到一起处理
dp[0/1][i][x][y]表示A/B选了当前的大质数,现在枚举到具有当前大质数的第i个数,之前A选中的集合为x,B选中的集合为y的方案数
dp[0/1][0][x][y]=f[i-1][x][y]
dp[0][i][x][y]=dp[0][i-1][x][y]+dp[0][i-1][x-S][y]
dp[1][i][x][y]=dp[1][i-1][x][y]+dp[1][i-1][x][y-S]
f[i][x][y]表示考虑了前i个大质数,A选中的集合为x,B选中的集合为y的方案数
f[i][x][y]=dp[0][num[i]][x][y]+dp[1][num[i]][x][y]-f[i-1][x][y]
由于不选当前大质数的方案被计算了2次,所以要-1
空间可用01背包的方法优化掉一维,但要注意枚举顺序
注:这道题指数只需要存到19,因为23*29>500
能想出这道题的人都好厉害呀
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> using namespace std; struct yts { int p,a; }q[510]; bool cmp(yts x,yts y) { return x.p<y.p; } int pri[10]={2,3,5,7,11,13,17,19}; int dp[2][1<<8][1<<8],f[1<<8][1<<8]; int n,m,mod,tot; void calc(int x) { tot++; for (int i=0;i<=7;i++) if (x%pri[i]==0) { while (x%pri[i]==0) x/=pri[i]; q[tot].a|=(1<<i); } q[tot].p=x; } int main() { scanf("%d%d",&n,&mod); for (int i=2;i<=n;i++) calc(i); sort(q+1,q+tot+1,cmp); f[0][0]=1; for (int i=1;i<=tot;i++) { if (q[i].p==1 || q[i].p!=q[i-1].p) for (int x=0;x<(1<<8);x++) for (int y=0;y<(1<<8);y++) dp[0][x][y]=dp[1][x][y]=f[x][y]; for (int x=(1<<8)-1;x>=0;x--) for (int y=(1<<8)-1;y>=0;y--) { if ((y&q[i].a)==0) dp[0][x|q[i].a][y]=(dp[0][x|q[i].a][y]+dp[0][x][y])%mod; if ((x&q[i].a)==0) dp[1][x][y|q[i].a]=(dp[1][x][y|q[i].a]+dp[1][x][y])%mod; } if (q[i].p==1 || i==n || q[i].p!=q[i+1].p) for (int x=0;x<(1<<8);x++) for (int y=0;y<(1<<8);y++) f[x][y]=((long long)dp[0][x][y]+dp[1][x][y]-f[x][y]+mod)%mod; } int ans=0; for (int i=0;i<(1<<8);i++) for (int j=0;j<(1<<8);j++) if ((i&j)==0) ans=(ans+f[i][j])%mod; printf("%d\n",ans); return 0; }
时间: 2024-10-12 19:42:28