[BZOJ 4197][Noi2015]寿司晚宴:状压DP

13 篇文章 0 订阅

点击这里查看原题

因为小于sqrt(500)的素数只有8个,而一个数大于sqrt(500)的素因子最多只有一个,因此可以将所有数分类,用一个二进制的8位数表示出现了哪些素因子,kind表示这个数的大于sqrt(500)的素因子,若不存在,则kind=1。将所有数按kind排序。
进行状压DP,f[i][j]表示1采用i方案,2采用j方案的方法数;p[i][j][k]表示该第i个人操作,1采用i方案,2采用j方案的方法书。每次如果取出的数的kind发生改变,则将f数组复制到p[1]和p[2],最后用p数组更新f数组,f[j][k]=p[1][j][k]+p[2][j][k]-f[j][k]。
注意,任何取模操作和位运算都要小心,偷懒容易导致WA。

/*
User:Small
Language:C++
Problem No.:4197
*/
#include<bits/stdc++.h>
#define ll long long
#define inf 999999999
using namespace std;
int n,mod,prime[8]={2,3,5,7,11,13,17,19},f[256][256],p[3][256][256],ans;
struct no{
    int se,typ;
}num[505];
bool cmp(no a,no b){
    return a.typ==b.typ?a.se<b.se:a.typ<b.typ;
}
int main(){
    freopen("data.in","r",stdin);//
    scanf("%d%d",&n,&mod);
    for(int i=1;i<=n;i++){
        int tmp=i;
        for(int j=0;j<8;j++){
            if(tmp%prime[j]==0){
                num[i].se|=1<<j;
                while(tmp%prime[j]==0) tmp/=prime[j];
            }
        }
        num[i].typ=tmp;
    }
    sort(num+2,num+n+1,cmp);
    f[0][0]=1;
    for(int i=2;i<=n;i++){
        if(i==2||num[i].typ==1||num[i].typ!=num[i-1].typ){
            memcpy(p[1],f,sizeof(p[1]));
            memcpy(p[2],f,sizeof(p[2]));
        }
        for(int j=255;j>=0;j--){
            for(int k=255;k>=0;k--){
                if((num[i].se&k)==0) p[1][j|num[i].se][k]=(p[1][j|num[i].se][k]+p[1][j][k])%mod;
                if((num[i].se&j)==0) p[2][j][k|num[i].se]=(p[2][j][k|num[i].se]+p[2][j][k])%mod;
            }
        }
        if(i==n||num[i].typ==1||num[i].typ!=num[i+1].typ){
            for(int j=0;j<256;j++)
                for(int k=0;k<256;k++)
                    f[j][k]=((p[1][j][k]+p[2][j][k]-f[j][k])%mod+mod)%mod;
        }
    }
    for(int i=0;i<256;i++)
        for(int j=0;j<256;j++)
            if((i&j)==0) ans=(ans+f[i][j])%mod;
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值