【bzoj4197】[Noi2015]寿司晚宴 状压DP

来源:互联网 发布:qt淘宝刷单平台哪个好 编辑:程序博客网 时间:2024/04/29 03:46

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

Source


题意:求满足以下条件的集合对(A,B)的数量:
(1) A⊆[2,n], B ⊆[2,n] (2) A∩B=∅ (3) ∀x∈A, y∈B, gcd(x,y)=1

首先看30分。

2~30的质数至多有10个,而两个集合不能选相同的质数,所以可以状压一下:
dp[i][x][y]表示选到第i个数,第一个人选的素数状态是x,第二个人选的素数状态是y的方案数,每次像01背包来转移就能优化掉第一维,然后ans累加就行了。

然后再看100分:

小于n的质数至多有八个,所以可以状压一下。对于一个数n,大于n的质数至多有1个,所以可以顺序DP。

把大质数相同的数放到一个集合里(排一下序),用dp[x][y][0/1]表示第一个人状态是x,第二个人状态是y,大质数分给第一个人/第二个人的方案数。然后状压那里还是像30分算法那样转移状态,唯一的区别就是加个第三维…

f[x][y]表示第一个人x,第二个人y,选了前i个大质数的方案数。i可以01背包那样优化掉。
然后每次当前大质数的集合开始时,把f复制一份给dp数组让他转移。
结束时,f[x][y]=dp[x][y][0]+dp[x][y][1]f[x][y]累加。

这是30分:

#include<cstdio>#include<cstring>#include<algorithm>#include<iostream>using namespace std;const int SZ = 5010;const int MAXC = (1 << 11) - 1;int pri[233] = {0,2,3,5,7,11,13,17,19,23,29}; //10int n,mod;int a[SZ],g[SZ],dp[SZ][SZ];void print(int x){    for(int i = 10;i >= 0;i --)        printf("%d",x >> i & 1);    printf(" ");}void calc(int x){    int xx = x;    for(int i = 1;i <= 10;i ++)    {        int v = pri[i];        if(x % v == 0)        {            while(x % v == 0) x /= v;            a[xx] |= (1 << i);        }    }}int main(){    scanf("%d%d",&n,&mod);    for(int i = 1;i <= n;i ++)        calc(i);//  for(int i = 0;i <= MAXC;i ++)//      print(i),printf("%d\n",g[i]);    dp[0][0] = 1;    for(int i = 2;i <= n;i ++)    {        for(int j = MAXC;j >= 0;j --)        {            for(int k = MAXC;k >= 0;k --)            {                if((a[i] & k) == 0) dp[j | a[i]][k] = (dp[j | a[i]][k] + dp[j][k]) % mod;                if((a[i] & j) == 0) dp[j][k | a[i]] = (dp[j][k | a[i]] + dp[j][k]) % mod;            }        }    }    int ans = 0;    for(int i = MAXC;i >= 0;i --)    {        for(int j = MAXC;j >= 0;j --)        {            if((i & j) == 0)            {            //  if(dp[i][j])            //      print(i),print(j),printf("%d\n",dp[i][j]);                ans = (ans + dp[i][j]) % mod;            }        }    }    printf("%d",ans);    return 0;}

100分:

#include<cstdio>#include<cstring>#include<algorithm>#include<iostream>using namespace std;typedef long long LL;const int SZ = 5010;const int MAXC = 1 << 8;int pri[233] = {2,3,5,7,11,13,17,19}; //10int n,mod;struct haha{    int a,p;}l[SZ];bool operator <(haha a,haha b){    return a.p < b.p;}int dp[SZ][SZ][2];int f[SZ][SZ];void print(int x){    for(int i = 10;i >= 0;i --)        printf("%d",x >> i & 1);    printf(" ");}void calc(int x){    int xx = x;    for(int i = 0;i < 8;i ++)    {        int v = pri[i];        if(x % v == 0)        {            while(x % v == 0) x /= v;            l[xx].a |= (1 << i);        }    }    l[xx].p = x;}int main(){    scanf("%d%d",&n,&mod);    for(int i = 2;i <= n;i ++)        calc(i);//  for(int i = 0;i <= MAXC;i ++)//      print(i),printf("%d\n",g[i]);    sort(l + 2,l + n + 1);    f[0][0] = 1;    for(int i = 2;i <= n;i ++)    {        if(i == 2 || l[i].p == 1 || l[i].p != l[i - 1].p)            for(int j = MAXC;j >= 0;j --)                for(int k = MAXC;k >= 0;k --)                    dp[j][k][0] = dp[j][k][1] = f[j][k];            for(int j = MAXC;j >= 0;j --)        {            for(int k = MAXC;k >= 0;k --)            {                if((l[i].a & k) == 0)                     dp[j | l[i].a][k][0] = (dp[j | l[i].a][k][0] + dp[j][k][0]) % mod;                if((l[i].a & j) == 0)                     dp[j][k | l[i].a][1] = (dp[j][k | l[i].a][1] + dp[j][k][1]) % mod;            }        }        if(i == n || l[i].p == 1 || l[i].p != l[i + 1].p)            for(int j = MAXC;j >= 0;j --)                for(int k = MAXC;k >= 0;k --)                    f[j][k] = ((LL)dp[j][k][0] + dp[j][k][1] - f[j][k]) % mod;    }    int ans = 0;    for(int i = MAXC;i >= 0;i --)    {        for(int j = MAXC;j >= 0;j --)        {            if((i & j) == 0)            {                ans = (ans + f[i][j]) % mod;            }        }    }    printf("%d",(ans + mod) % mod);    return 0;}/*30 30*/
1 0