BZOJ4197 [Noi2015]寿司晚宴 【状压dp】

题目链接

BZOJ4197

题解

两个人选的数都互质,意味着两个人选择了没有交集的质因子集合
容易想到将两个人所选的质因子集合作为状态\(dp\)

\(n\)以内质数很多,但容易发现\(\sqrt{n} \approx 22.3\),这里边的质数只有\(8\)个,而大于\(\sqrt{n}\)的质因子只会出现一次,可以特殊讨论
我们先将所有数按最大质因子排序,如果最大质因子在那\(8\)个质数里边,就记为\(1\)
我们设\(f[i][s1][s2]\)表示选了前\(i\)个数,两人的质因子集合分别为\(s1\)\(s2\)的方案数
那么显然

\[ans = \sum\limits_{s1 \cap s2 = \emptyset} f[n][s1][s2] \]

对于最大质因子不超过\(sqrt{n}\)的那些数,我们可以枚举加入\(s1\)\(s2\)进行转移
对于同一组最大质因数为\(x\)的数,我们先将\(f\)拷贝到\(g\)中,令\(g[0|1][i][s1][s2]\)表示该最大质因数加入谁中,选了前\(i\)个数,质因子集合为\(s1\)\(s2\)的方案数
求完后令\(f[i][s1][s2] = g[0][i][s1][s2] + g[1][i][s1][s2] - f[i][s1][s2]\),因为原来的\(f\)是不包含这一组数的方案,而两个\(g\)都包含了不包含这一组数的方案数,要减去一个

由于空间问题,\(f\)\(g\)都要通过逆序转移压掉\(i\)那一维

然后就做完了

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
using namespace std;
const int maxn = 505,maxm = 1 << 8,INF = 1000000000;
inline int read(){
	int out = 0,flag = 1; char c = getchar();
	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
	return out * flag;
}
int f[maxm][maxm],g[2][maxm][maxm];
int n,P,a[maxn];
int p[] = {2,3,5,7,11,13,17,19};
struct node{
	int x,p;
}e[maxn];
inline bool operator <(const node& a,const node& b){
	return a.p < b.p;
}
int getp(int x){
	for (int i = 0; i < 8; i++)
		if (x % p[i] == 0) while (x % p[i] == 0) x /= p[i];
	return x;
}
void init(){
	for (int i = 2; i <= n; i++)
		for (int k = 0; k < 8; k++)
			if (i % p[k] == 0)
				a[i] |= (1 << k);
}
int main(){
	n = read(); P = read(); init();
	for (int i = 2; i <= n; i++) e[i].x = i,e[i].p = getp(i);
	sort(e + 2,e + 1 + n);
	f[0][0] = 1;
	for (int i = 2; i <= n; i++){
		int x = e[i].x,s = a[x];
		if (e[i].p == 1){
			for (int j = maxm - 1; ~j; j--){
				for (int k = maxm - 1; ~k; k--){
					int t = f[j][k];
					f[j][k | s] = (f[j][k | s] + t) % P;
					f[j | s][k] = (f[j | s][k] + t) % P;
				}
			}
		}
		else {
			for (int j = 0; j < maxm; j++)
				for (int k = 0; k < maxm; k++)
					g[0][j][k] = g[1][j][k] = f[j][k];
			int nxt = i;
			while (nxt < n && e[nxt + 1].p == e[i].p) nxt++;
			for (int l = 0; l <= nxt - i; l++){
				x = e[l + i].x,s = a[x];
				for (int j = maxm - 1; ~j; j--){
					for (int k = maxm - 1; ~k; k--){
						g[0][j | s][k] = (g[0][j | s][k] + g[0][j][k]) % P;
						g[1][j][k | s] = (g[1][j][k | s] + g[1][j][k]) % P;
					}
				}
			}
			i = nxt;
			for (int j = 0; j < maxm; j++)
				for (int k = 0; k < maxm; k++)
					f[j][k] = (((g[1][j][k] + g[0][j][k]) % P - f[j][k]) % P + P) % P;
		}
	}
	int ans = 0;
	for (int j = 0; j < maxm; j++)
		for (int k = 0; k < maxm; k++)
			if (!(j & k)) ans = (ans + f[j][k]) % P;
	printf("%d\n",ans);
	return 0;
}

posted @ 2018-06-08 18:32  Mychael  阅读(163)  评论(0编辑  收藏  举报