【NOI2015】【bzoj4197】【状压DP】【滚动数组】寿司晚宴

来源:互联网 发布:次日留存算法 编辑:程序博客网 时间:2024/04/29 02:44

Description

为了庆祝 NOI 的成功开幕,主办方为大家准备了一场寿司晚宴。小 G 和小 W 作为参加 NOI 的选手,也被邀请参加了寿司晚宴。
在晚宴上,主办方为大家提供了 n−1 种不同的寿司,编号 1,2,3,…,n−1,其中第 i 种寿司的美味度为 i+1 (即寿司的美味度为从 2 到 n)。
现在小 G 和小 W 希望每人选一些寿司种类来品尝,他们规定一种品尝方案为不和谐的当且仅当:小 G 品尝的寿司种类中存在一种美味度为 x 的寿司,小 W 品尝的寿司中存在一种美味度为 y 的寿司,而 x 与 y 不互质。
现在小 G 和小 W 希望统计一共有多少种和谐的品尝寿司的方案(对给定的正整数 p 取模)。注意一个人可以不吃任何寿司。

Input

输入文件的第 1 行包含 2 个正整数 n,p,中间用单个空格隔开,表示共有 n 种寿司,最终和谐的方案数要对 p 取模。

Output

输出一行包含 1 个整数,表示所求的方案模 p 的结果。

Sample Input

3 10000

Sample Output

9

HINT

2≤n≤500
0< p≤1000000000

状态压缩DP,我写的O(n×48)好像还有一个O(n×38)我不会,网上说得简洁,我蒟蒻无比,看不懂,就只会写这个48
然后大概说一下怎么DP
我们选取一个数,实际上就是选它的质因数,而500以内质数有95个明显无法装压,但我们还可以发现,500以内每个数大于500的质因子最多只有一个,而小于500的质数一共只有8个,我们就可以考虑装压了
我们建立两个数组f[k][i][j]表示考虑前k个数,A的状态为i,B的状态为j有多少个情况,dp[0/1][k][i][j]表示正在考虑第k个数,A/B拿走第k个数,拿走后A状态为i,B为j


然后转移方程可以列出:
dp[0][k][i|p(k)][j]=f[k-1][i|p(k)][j]+dp[0][k][i][j]
dp[1][k][i][j|p(k)]=f[k-1][i][j|p(k)]+dp[1][k][i][j]
f[k][i][j]=dp[0][k][i][j]+dp[1][k][i][j]-f[k-1][i][j]


但是数组开不下,我们就可以滚动,这时才有了我一直没看懂的转移方程
dp[0][i|p(k)][j]=dp[0][i|p(k)][j]+dp[0][i][j]
dp[1][i][j|p(k)]=dp[1][i][j|p(k)]+dp[1][i][j]
f[i][j]=dp[0][i][j]+dp[1][i][j]-f[i][j]
然后我们先处理前面全部属于那8个质数的,再一个一个大质数的枚举,处理就可以了


程序如下:

#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<set>#include<map>#include<queue>#include<algorithm>#include<vector>#include<cstdlib>#include<cmath>#include<ctime>#include<stack>#define INF 2100000000#define ll long long#define clr(x)  memset(x,0,sizeof(x))#define clrmax(x)  memset(x,127,sizeof(x))using namespace std;inline int read(){    char c;    int ret=0;    while(!(c>='0'&&c<='9'))        c=getchar();    while(c>='0'&&c<='9')    {        ret=(c-'0')+(ret<<1)+(ret<<3);        c=getchar();    }    return ret;}#define M 500#define N (1<<8)int f[N][N],dp[2][N][N],n,P,t;const int pri[]={2,3,5,7,11,13,17,19};struct node{    int x,num;    node(int x=0,int num=0):x(x),num(num){}}a[M];#define x(y) a[y].x#define num(x) a[x].numbool com(node a,node b){    return a.x<b.x;}void add(int x){    int temp=0;    for(int i=0;i<8;i++)        while(x%pri[i]==0)        {            x/=pri[i];            temp|=1<<i;        }    a[++t]=node(x,temp);}void DP(){    f[0][0]=1;    for(int l=1,r;l<=t;l=r+1)    {        for(r=l;x(r+1)==x(r)&&x(r)!=1;r++);        memcpy(dp[0],f,sizeof(f));memcpy(dp[1],f,sizeof(f));        for(int k=l;k<=r;k++)        {            for(int i=255;~i;i--)            {                int now=255^i;                for(int j=now;;j=(j-1)&now)                {                    if((j&num(k))==0)                        dp[0][i|num(k)][j]=(dp[0][i|num(k)][j]+dp[0][i][j])%P;                    if((i&num(k))==0)                        dp[1][i][j|num(k)]=(dp[1][i][j|num(k)]+dp[1][i][j])%P;                    if(!j)break;                }            }        }        for(int i=0;i<=255;i++)            for(int j=0;j<=255;j++)                f[i][j]=((dp[0][i][j]+dp[1][i][j]-f[i][j])%P+P)%P;    }    int ans=0;    for(int i=0;i<=255;i++)    {        int now=255^i;        for(int j=now;;j=(j-1)&now)        {            ans=(ans+f[i][j])%P;            if(!j)break;        }    }    printf("%d",ans);}int main(){    //freopen("in.txt","r",stdin);    n=read();P=read();    for(int i=2;i<=n;i++)        add(i);    sort(a+1,a+t+1,com);    DP();    return 0;}

大概就是这个样子,如果有什么问题,或错误,请在评论区提出,谢谢。

0 0