[Noi2015]寿司晚宴

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

首先说一句:寿司真好吃(逃

咳,我们回归正题。。。首先看着题就没法状压,\(n\leqslant 500\)完全下不去手。。。然后考虑互质的话,我们有一个套路做法,就是分解质因数。不过500内的质因数也有上百个,还是压不了。。。不过,我们发现一件事情,有很多质因数在每个数里至多只会出现一次!!!

好,我们缕一下思路,首先,有一些质数会在一个数中出现很多次,他们都\(\leqslant\sqrt{500}\),这些质数共计8个。我们对这8个数进行状压,然后剩下的那些质因数,由于它们至多在一个数中出现一次,那么就说明,它们当中有一个被一个人选了,另一个人就不能选这些数了。

好,我们再次缕一下思路,我们把这些拥有相同大质因数的数放到一堆来,类似于分块的思想。然后记\(F[S1][S2]\)代表小G选的寿司中,前8个质数的状态为S1;小W选的寿司中,前8个质数的状态为S2。然后这样不好在块内转移,我们就再开个\(G[2][S1][S2]\),每到一个块中,\(G[0]=G[1]=F\),然后\(G[0]\)代表当前块所代表的大质数被小G选了,\(G[1]\)表示大质数被小W选了。

然后这个块处理完之后,\(F=G[0]+G[1]-F\),-F是因为不选的情况被考虑了两次,所以要减掉

具体的,看代码吧。。。

/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline int read(){
    int x=0,f=1;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar())  if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar())    x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}
inline void print(int x){
    if (x>=10)  print(x/10);
    putchar(x%10+'0');
}
const int N=5e2;
const int prime[8]={2,3,5,7,11,13,17,19};
struct S1{
    int sta,p;
    bool operator <(const S1 &x)const{return p<x.p;}
}A[N+10];
int f[(1<<8)+10][(1<<8)+10];
int g[2][(1<<8)+10][(1<<8)+10];
int n,p,All;
int main(){
    n=read(),p=read(),All=(1<<8)-1;
    for (int i=2;i<=n;i++){
        int x=i;
        for (int k=0;k<8;k++){
            if (x%prime[k]==0)  A[i].sta|=1<<k;//记录前8个质数的状态
            while (x%prime[k]==0)   x/=prime[k];
        }
        A[i].p=x;//记录大质数,为1代表没有大质数
    }
    sort(A+2,A+1+n);
    f[0][0]=1;
    for (int i=2;i<=n;i++){
        if (i==2||A[i].p!=A[i-1].p||A[i].p==1){//如果是块的开始,或者没有大质数,就令g[0]=g[1]=f
            memcpy(g[0],f,sizeof(f));
            memcpy(g[1],f,sizeof(f));
        }
        for (int j=All;~j;j--){
            for (int k=All;~k;k--){
                if (j&k)    continue;
                if (!(A[i].sta&k))  g[0][j|A[i].sta][k]=(g[0][j|A[i].sta][k]+g[0][j][k])%p;//小G选了不会和小W有冲突
                if (!(A[i].sta&j))  g[1][j][k|A[i].sta]=(g[1][j][k|A[i].sta]+g[1][j][k])%p;//和上面同理
            }
        }
        if (i==n||A[i].p!=A[i+1].p||A[i].p==1)//如果是块的结束,或者没有大质数,则令F=G[0]+G[1]-F
            for (int j=All;~j;j--)
                for (int k=All;~k;k--)
                    if (!(j&k))
                        f[j][k]=((g[0][j][k]+g[1][j][k]-f[j][k])%p+p)%p;
    }
    int Ans=0;
    for (int j=All;~j;j--)
        for (int k=All;~k;k--)
            if (!(j&k))
                Ans=(Ans+f[j][k])%p;
    printf("%d\n",Ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Wolfycz/p/9669681.html