ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【题解】[NOI2015] 寿司晚宴

2022-02-03 18:35:12  阅读:163  来源: 互联网

标签:f1 f2 寿司 int s2 s1 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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有