其他分享
首页 > 其他分享> > 【题解】[NOI2015] 寿司晚宴

【题解】[NOI2015] 寿司晚宴

作者:互联网

链接
能学好多东西
题意描述:

两个人各从正整数 2 ~ n(<=500) 中取一些数,可以不取;若取出的两个集合中,任意属于不同集合的的两个元素都互质,则方案合法;求合法方案数

首先注意到一个方案合法的等价条件是:两个人的质数集合没有交集
可以想到 \(f[s1][s2]\) 表示两个人的质数集合为 s1 和 s2 \((s1\and s2=0)\) 时的方案数,当然还有隐藏的一维:前 i 个数
设第 i 个数的质数集合为 s,则有递推式:

\[f[i][s1|s][s2] += f[i-1][s1][s2], \ (s\and s2=0) \\ f[i][s1][s2|s] += f[i-1][s1][s2], \ (s\and s1=0) \]

但是 n=500 时显然状压不下那么多的质数
我们需要注意到一个性质:对于一个数 \(x\),最多只有一个大于 \(\sqrt x\) 的质因子
对应此题,即所有数最多只有一个大于 22 的质因子,而小于 22 的质数只有 8 个
我们可以考虑只状压前 8 个质数,对于更大的质数的不重复取,用排序来处理:那个大质数,要么属于集合 s1,要么 s2,要么都不属于
对每个数字,预处理得到其质因子的前 8 个质数的集合 s,和唯一一个大质数 p(无则设为 1)
所有数字按 p 排序,然后递推:
设 \(f[i][s1][s2]\) 表示前 i 个数,(前 8 个)质数集合为 s1, s2 的方案数,设 \(f1[i][s1][s2]\) 表示当前的大质数只可能由第 1 个人取的方案数,\(f2[i][s1][s2]\) 同理
则对于当前出现的一个新的大质数(对应的一段区间),首先将 f 复制给 f1, f2,然后 f1 和 f2 按各自的规则递推,最后再相加赋值给 f;两者都不包括当前质数的情况算了两次,所以要扣除一倍 f:\(f[s1][s2] = f1[s1][s2] + f2[s1][s2] - f[s1][s2]\)

\[f1[i][s1|s][s2] += f1[i-1][s1][s2], \ (s\and s2=0) \\ f2[i][s1][s2|s] += f2[i-1][s1][s2], \ (s\and s1=0) \]

实现的时候我们可以不用滚动数组,注意到被贡献的 s1 始终小等于被修改的 s1|s(且每个数只被贡献出去一次),所以可以从大往小枚举 s1, s2

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int pri[8] = {2, 3, 5, 7, 11, 13, 17, 19};
const int INF = 260;
int N; ll mod;
ll f[INF][INF], f1[INF][INF], f2[INF][INF];
struct node {
	int s, p;
	void cal(int x) {
		s = 0;
		for (int i=0; i< 8; i++) if (x%pri[i]==0) {
			s |= (1<<i);
			while (x%pri[i]==0) x /= pri[i];
		}
		p = x;
	}
} P[505];
int cmp(node x, node y) { return x.p < y.p; }
void add(ll &x, ll y) { x += y; if (x> mod) x -= mod; }
int main()
{
	scanf("%d%lld", &N, &mod);
	for (int i=2; i<=N; i++) P[i-1].cal(i);
	sort(P+1, P+N, cmp);
	f[0][0] = 1;
	for (int i=1; i< N; i++) {
		if (P[i].p==1 || P[i].p!=P[i-1].p)
			memcpy(f1, f, sizeof(f)), memcpy(f2, f, sizeof(f));
		int s = P[i].s;
		for (int s1=255; s1>=0; s1--) {
			for (int s2=255; s2>=0; s2--) {
				if (s1&s2) continue;
				if (!(s&s2)) add(f1[s1|s][s2], f1[s1][s2]);
				if (!(s&s1)) add(f2[s1][s2|s], f2[s1][s2]);
				// [s1][s2] is contributed only once
			}
		}
		if (i==N-1 || P[i].p==1 || P[i].p!=P[i+1].p) {
			for (int s1=0; s1<=255; s1++) {
				for (int s2=0; s2<=255; s2++) {
					if (s1&s2) continue;
					f[s1][s2] = (f1[s1][s2]+f2[s1][s2]+mod-f[s1][s2])%mod;
				}
			}
		}
	}
	ll ans = 0;
/*	for (int s1=0; s1<=3; s1++)
		for (int s2=0; s2<=3; s2++)
			printf("f[%d][%d]=%lld\n", s1, s2, f[s1][s2]);*/
	for (int s1=0; s1<=255; s1++)
		for (int s2=0; s2<=255; s2++) add(ans, f[s1][s2]);
	printf("%lld\n", ans);
}

标签:f1,f2,寿司,int,s2,s1,NOI2015,题解,质数
来源: https://www.cnblogs.com/zhyh/p/15861793.html