ICode9

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

卡牌

2022-04-24 22:00:06  阅读:182  来源: 互联网

标签:int 质数 卡牌 41 MAXN 倍数 sum


题意:

现在有 \(m\) 轮游戏,第 \(i\) 轮游戏会给出 \(c_i\) 个质数,小 A 需要选择任意多张卡牌,使得这些卡牌上面的正整数的乘积能被该轮游戏给出的每个质数整除。

问有多少种卡牌的选法。

暴力容斥

考虑 \(s_i \leq 30\) 时的做法。

目标:使每个质数被整除的方案数,求所有属性集合的交集。

属性:满足是第 \(i\) 个质数的倍数的方案数。

求属性的交集,等于 全集 - 属性补集的并集

要求不是 \(S\) 中任意一个质数的数的个数 \(c\),那么方案数就是 \(2^c\)。

直接容斥:

\[\text{ans} = |U| - |\bigcup_{S \subset U, S \neq \emptyset} S| \]

\[= |U| - \sum_{S \subset U, S \neq \emptyset} (-1)^{|S|}2^{|S|} \]

质数性质

发现 \(s > 41\) 后的质因数只会出现一次,对于同一个 \(> 41\)的 质因数的倍数可以分成一个集合,记个数为 \(c\)。

如果给出了这个质因数,那么最终答案乘 \(2^{c} - 1\),表示必选至少一个这样的数。

如果没有给出,就乘 \(2^{c} - 1\)。

考虑到实际上,每个这样的数还有 \(\leq 41\) 的质因子,会对选择前 \(13\) 个质数产生影响。

在容斥的时候, 假设当前要求的属性集合 \(S\), 求满足不是集合中任意一个质数的倍数的方案数,求出了有 \(c\) 个数。

设 \(>41\) 的某个质数及其倍数中满足的有 \(d\) 个,假如它被强制选了,那么最后的方案数就是 \(2^{c - d}(2^d - 1)\)。

否则就是 \(2^{c - d}2^d = 2^c\)。

只要快速求出这 \(>41\) 的质数及其倍数在满足某些条件下的个数就能算这个东西。

于是使用状压 dp。

\(f_{s, i}\), 表示满足不是集合 \(s\) 中任意一个质数的倍数,是质数 \(i\) 或其倍数的个数。

算出这个就能直接按照上面的容斥算出方案数。

代码


#include<bits/stdc++.h>
using namespace std;
 
using ll = long long;
using ull = unsigned long long; 
const int MAXN = 2010;
template <typename T>
void Read(T &x) {
	x = 0; T f = 1; char a = getchar();
	for(; a < '0' || '9' < a; a = getchar()) if (a == '-') f = -f;
	for(; '0' <= a && a <= '9'; a = getchar()) x = (x * 10) + (a ^ 48);
	x *= f;
}	

const int mod = 998244353; 
inline int add(const int &a, const int &b) {
	static int c; 
	c = a + b;
	if (c >= mod) c -= mod;
	return c; 
} 
int dec(int a, int b) {
	int c = a - b;
	if (c < 0) c += mod;
	return c; 
}
inline int mul(const int &a, const int &b) {
	return 1ll * a * b % mod; 
}
int qpow(int a, int b) {
	int sum = 1;
	while (b) {
		if (b & 1) sum = mul(sum, a);
		a = mul(a, a);
		b >>= 1;
	}
	return sum; 
}

int len; 
int id[MAXN];
vector<int> d[MAXN];
int prime[MAXN], s[MAXN];  
bool p[MAXN]; 
int f[(1 << 13) + 10][MAXN]; 
int cnt[(1 << 13) + 10], tot[MAXN]; 

void init() {
	for (int i = 2; i <= 2000; i ++) {
		if (!p[i]) {
			prime[++ len] = i; 
			id[i] = len - 1; 
		}
		for (int j = i * 2; j <= 2000; j += i)
			p[j] = 1; 
	}
	for (int i = 2; i <= 2000; i ++)
		for (int j = 1; j <= len; j ++)
			if (i % prime[j] == 0) {
				if (j <= 13) s[i] |= 1 << (j - 1); 
				d[i].emplace_back(prime[j]); 
			}
}

int main() {

	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0); 
	init(); 
	int n, m; 
	cin >> n;
	for (int i = 1; i <= n; i ++) {
		int x; 
		cin >> x; 
		tot[x] ++; 
	}
	
	for (int i = 0; i < (1 << 13); i ++) {
		for (int j = 2; j <= 2000; j ++) {
			if (!(s[j] & i)) {
				f[i][d[j].back()] = add(f[i][d[j].back()], tot[j]); 
				cnt[i] = add(cnt[i], tot[j]); 
			}
		}
	}
	
	cin >> m;
	while (m --) {
		int len; 
		cin >> len;
		vector<int> b(len);
		int S = 0; 
		for (auto &i : b) {
			cin >> i; 
			if (i <= 41) 
				S |= 1 << id[i]; 
		}
		int ans = 0; 
		for (int i = 0; i < (1 << 13); i ++) {
			if ((i | S) != S) continue; 
			int prod = 1, ct = cnt[i];
			for (auto j : b) {
				if (j <= 41) continue; 
				prod = mul(prod, qpow(2, f[i][j]) - 1); 
				ct -= f[i][j];
			}
			prod = mul(prod, qpow(2, ct)); 
			ans = add(ans, mul(((__builtin_popcount(i) & 1)? mod - 1 : 1), prod)); 
		}
		cout << ans << endl; 
	}
	return 0; 
}

标签:int,质数,卡牌,41,MAXN,倍数,sum
来源: https://www.cnblogs.com/qjbqjb/p/16187858.html

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

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

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

ICode9版权所有