BZOJ 4197: [Noi2015]寿司晚宴 状态压缩 + 01背包

4197: [Noi2015]寿司晚宴

Time Limit: 10 Sec  Memory Limit: 512 MB

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

 

题解:

  前8个质因子,状压

  这n<=500的数都最多包含一个大于根号n的质因子

  跑01背包

#include<bits/stdc++.h>
using namespace std;
#define ls i<<1
#define rs ls | 1
#define mid ((ll+rr)>>1)
#define pii pair<int,int>
#define MP make_pair
typedef long long LL;
typedef unsigned long long ULL;
const long long INF = 1e18+1LL;
const double pi = acos(-1.0);
const int N=2000+20,M=1e6+10,inf=2147483647;

int p[] = {2,3,5,7,11,13,17,19};
int dp[2][(1<<8)+10][(1<<8)+10],pd[2][(1<<8)+10][(1<<8)+10],n,P,f[N],has[(1<<8)+10][(1<<8)+10];
vector<int > fi,se[N];
int main() {
    scanf("%d%d",&n,&P);
    for(int i = 2; i <= n; ++i) {
        int tmp = i,now = 0;
        for(int j = 0; j < 8; ++j)
            while(tmp%p[j] == 0) now|=(1<<j),tmp/=p[j];
        if(tmp == 1)
            f[i] = now,fi.push_back(i);
        else
            f[i] = now,se[tmp].push_back(i);
    }
    int now = 0;
    dp[0][0][0] = 1;
    for(int g = 0; g < fi.size(); ++g) {
        int u = fi[g];
        now ^= 1;
        memset(dp[now],0,sizeof(dp[now]));
        for(int i = 0; i < (1<<8); ++i)
            for(int j = 0; j < (1<<8); ++j) {
                if((j&f[u])==0) dp[now][i|f[u]][j] += dp[now^1][i][j]%P,dp[now][i|f[u]][j]%=P;
                if((i&f[u])==0) dp[now][i][j|f[u]] += dp[now^1][i][j]%P,dp[now][i][j|f[u]]%=P;
                dp[now][i][j] += dp[now^1][i][j]%P,dp[now][i][j]%=P;
            }
    }
    for(int S = 2; S <= n; ++S) {
        if(se[S].size() == 0) continue;
        int tmp = now;
        for(int i = 0; i < (1<<8); ++i)
            for(int j = 0; j < (1<<8); ++j) has[i][j] = pd[tmp][i][j] = dp[now][i][j];

        for(int g = 0; g < se[S].size(); ++g) {
            now ^= 1;
            memset(dp[now],0,sizeof(dp[now]));
            for(int i = 0; i < (1<<8); ++i)
            for(int j = 0; j < (1<<8); ++j) {
                int u = se[S][g];
                if((i&f[u])==0) dp[now][i][j|f[u]] += dp[now^1][i][j]%P,dp[now][i][j|f[u]]%=P;
                dp[now][i][j] += dp[now^1][i][j]%P,dp[now][i][j]%=P;
            }
        }
        for(int g = 0; g < se[S].size(); ++g) {
            tmp ^= 1;
            memset(pd[tmp],0,sizeof(pd[tmp]));
            for(int i = 0; i < (1<<8); ++i)
            for(int j = 0; j < (1<<8); ++j) {
                int u = se[S][g];
                if((j&f[u])==0) pd[tmp][i|f[u]][j] += pd[tmp^1][i][j]%P,pd[tmp][i|f[u]][j]%=P;
                pd[tmp][i][j] += pd[tmp^1][i][j]%P,pd[tmp][i][j]%=P;
            }
        }
        for(int i = 0; i < (1<<8); ++i)
            for(int j = 0; j < (1<<8); ++j) dp[now][i][j] += ((pd[tmp][i][j] - has[i][j])%P+P)%P,dp[now][i][j] %= P;
    }
    int ans = 0;
    for(int i = 0; i < (1<<8); ++i)
        for(int j = 0; j < (1<<8); ++j) if((i&j) == 0)ans += dp[now][i][j],ans %= P;
    printf("%d\n",ans);
    return 0;
}

 

posted @ 2017-08-20 12:37  meekyan  阅读(216)  评论(0编辑  收藏  举报