【bzoj4197】[Noi2015]寿司晚宴 dp

来源:互联网 发布:天刀金石头加功力数据 编辑:程序博客网 时间:2024/03/29 23:22
因为每个数只有一个大于根号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;}


0 0
原创粉丝点击