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

8 篇文章 0 订阅

为了庆祝 NOI 的成功开幕,主办方为大家准备了一场寿司晚宴。小 G 和小 W 作为参加 NOI 的选手,也被邀请参加了寿司晚宴。

在晚宴上,主办方为大家提供了 n1 n − 1 种不同的寿司,编号 1,2,3,,n1 1 , 2 , 3 , … , n − 1 ,其中第 i i 种寿司的美味度为 i+1 (即寿司的美味度为从 2 2 n)。

现在小 G 和小 W 希望每人选一些寿司种类来品尝,他们规定一种品尝方案为不和谐的当且仅当:小 G 品尝的寿司种类中存在一种美味度为 x x 的寿司,小 W 品尝的寿司中存在一种美味度为 y 的寿司,而 x x y 不互质。

现在小 G 和小 W 希望统计一共有多少种和谐的品尝寿司的方案(对给定的正整数 p p 取模)。注意一个人可以不吃任何寿司。

输入格式

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

输出格式

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

样例一

input
3 10000

output
9

样例二

input
4 10000

output
21

样例三

input
100 100000000

output
3107203

限制与约定

测试点编号 n n 的规模约定
12n300 < p \leq 1000000000 0 < p \leq 1000000000
2
3
4 2n100 2 ≤ n ≤ 100
5
6 2n200 2 ≤ n ≤ 200
7
8 2n500 2 ≤ n ≤ 500
9
10

时间限制: 1s 1 s

空间限制: 512MB 512 MB


思路

    首先这题我们先根据数据范围来入手,由于要求两人选的数字互质,那也就是表示他们选择的数的质因子是不能有相等的,那我们就可以自然而然地想到每一个质因子用一个二进制位表示这个人选择的数中是否有这个因子,但因为500以内的质因子又有很多,直接状压时间空间复杂度都要爆炸。但又因为我们知道,一个数含有大于等于 n n 的因子只有0个或1个,所以我们其实只用将小于等于 n n 的因子筛出,在本题小于等于 22<500<23 22 < 500 < 23 的情况下只有8个质数,所以可以满足我们的要求。那么这题的状态表示是 f[i][j],i,j[0,1<<8) f [ i ] [ j ] , i , j ∈ [ 0 , 1 << 8 ) 表示第一个人选的数包含状态为i的质因子和第二个人选的数包含状态为j的质因子的方案数, g[0/1][i][j],i,j[0,1<<8) 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值