[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


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

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

好,我们缕一下思路,首先,有一些质数会在一个数中出现很多次,他们都(leqslantsqrt{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
",Ans);
	return 0;
}
原文地址:https://www.cnblogs.com/Wolfycz/p/9669681.html