[UOJ129][BZOJ4197][NOI2015]寿司晚宴(状压DP)

#129. 【NOI2015】寿司晚宴

为了庆祝 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 取模)。注意一个人可以不吃任何寿司。

输入格式

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

输出格式

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

样例一

input

3 10000

output

9

样例二

input

4 10000

output

21

样例三

input

100 100000000

output

3107203

限制与约定

测试点编号 n 的规模 约定
1 2 n 30 0 < p \leq 1000000000
2
3
4 2 n 100
5
6 2 n 200
7
8 2 n 500
9
10

时间限制: 1 s

空间限制: 512 MB


思路

    首先这题我们先根据数据范围来入手,由于要求两人选的数字互质,那也就是表示他们选择的数的质因子是不能有相等的,那我们就可以自然而然地想到每一个质因子用一个二进制位表示这个人选择的数中是否有这个因子,但因为500以内的质因子又有很多,直接状压时间空间复杂度都要爆炸。但又因为我们知道,一个数含有大于等于 n 的因子只有0个或1个,所以我们其实只用将小于等于 n 的因子筛出,在本题小于等于 22 < 500 < 23 的情况下只有8个质数,所以可以满足我们的要求。那么这题的状态表示是 f [ i ] [ j ] , i , j [ 0 , 1 << 8 ) 表示第一个人选的数包含状态为i的质因子和第二个人选的数包含状态为j的质因子的方案数, g [ 0 / 1 ] [ i ] [ j ] , i , j [ 0 , 1 << 8 ) 表示第一个人/第二个人取完这个数时,两个人状态分别为i,j时的方案数。于是我们就有了以下转移方程:
1. g[0][i|status][j]+=g[0][i][j],status and j ==0
2. g[1][i][j|status]+=g[1][i][j],status and i ==0
3. f[i][j]=g[0][i][j]+g[1][i][j]-f[i][j] (至于这一步还要减f[i][j]的原因是这个数两个人都不选时加多了,要减去)
4. ans+=f[i][j] i and j==0
    之后答案就是ans了。

Code

#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
/*================Header Template==============*/
const int prime[8]={2,3,5,7,11,13,17,19},cnt=8,lim=1<<8;
struct num {
    int x,y;
    inline bool operator < (const num &rhs) const {
        return y==rhs.y?x<rhs.x:y<rhs.y;
    }
}o[505];
int n,lst=2,p,f[lim][lim],g[2][lim][lim],ans;
inline void Add(int &x,int y) {
    x+=y;
    if(x>p)
        x-=p;
}
int main() {
    read(n);read(p);
    //int t=clock();
    for(int i=2;i<=n;i++) {
        int tmp=i;
        for(int j=0;j<cnt;j++) {
            if(!(tmp%prime[j])) {
                o[i].x|=(1<<j);
                while(!(tmp%prime[j]))
                    tmp/=prime[j];
            }
        }
        o[i].y=tmp;
    }
    sort(o+2,o+n+1);
    f[0][0]=1;
    for(int i=2;i<=n;i++) {
        if(i==2||o[i].y==1||o[i].y!=o[i-1].y)
            memcpy(g[0],f,sizeof f),memcpy(g[1],f,sizeof f);
        for(int j=lim-1;~j;j--)
            for(int k=lim-1;~k;k--) {
                if(!(j&o[i].x))
                    Add(g[1][j][k|o[i].x],g[1][j][k]);
                if(!(k&o[i].x))
                    Add(g[0][j|o[i].x][k],g[0][j][k]);
            }
        if(i==n||o[i].y==1||o[i].y!=o[i+1].y)
            for(int j=0;j<lim;j++)
                for(int k=0;k<lim;k++)
                    f[j][k]=((g[0][j][k]+g[1][j][k]-f[j][k])%p+p)%p;
    }
    for(int i=0;i<lim;i++)
        for(int j=0;j<lim;j++)
            if(!(i&j))
                Add(ans,f[i][j]);
    writeln(ans);
    //cerr<<clock()-t<<"ms"<<endl;
}

猜你喜欢

转载自blog.csdn.net/effervescence/article/details/79433792