Codeforces 1687E - Become Big For Me(Min-Max 容斥)

Codeforces 题面传送门 & 洛谷题面传送门

一道特别 nb 的题。

首先,题目要求的是 GCD,而我们拿得出手的是 LCM,这能给我们什么联想呢?Min-Max 容斥。考虑 Min-Max 容斥的扩展版本:\(\text{kth}(S)=\sum\limits_{T\subseteq S}(-1)^{|T|-k}\dbinom{|T|-1}{k-1}\max(T)\),其中 \(\text{kth}(S)\) 表示 \(S\) 中第 \(k\) 小的值。而题目所求本质上是对每个质因子求最小值与次小值之和,因此套用 Min-Max 容斥可以得到 \(\min(S)+\text{secmin}(S)=\sum\limits_{T\subseteq S}((-1)^{|T|-1}+(|T|-1)(-1)^{|T|-2})\max(T)\),化简一下可以得到 \(\sum\limits_{T\subseteq S}(|T|-2)(-1)^{|T|}\max(T)\)。也就是说一种暴力的构造方法是,枚举所有集合 \(T\),做 \((|T|-2)·(-1)^{|T|}\) 次 enlarge 操作(\(a(a<0)\) 次 enlarge 操作等价于 \(-a\) 次 reduce 操作),这样 \(\sum |b|\)\(2^nn^2\) 的,最多只能处理 \(n=14\),非常逊,因此我们需要优化它。

注意到 \(\max\limits_{n\le 10^6}\omega(n)=7\),刚好是 \(14\) 的一半,并且根据某经典结论,对于任意值域为 \(10^6\) 的集合,总存在大小 \(\le 8\) 的子集满足原集合的 GCD 等于该子集的 GCD,因此我们考虑借鉴该结论进行构造。先考虑《经典结论》的证法:随机选择一个数加入子集,枚举该数的所有质因子,再将集合中所有数中,该质因子出现次数最小的数加入集合,由于最多只有 \(7\) 个质因子,因此最多新增 \(7\) 个元素,集合大小也就进而 \(\le 8\)

这样一来构造方式就明显多了。由于这里我们要求最小值和次小值,因此我们不妨先随机选择两个数 \(x,y\) 加入集合,然后枚举 \(x,y\) 的质因子:

  • 如果该质因子是 \(x,y\) 的公共质因子,那么我们就另选两个数,使得这个质因子出现次数刚好是最小值和次小值,并加入集合。
  • 如果该质因子不是 \(x,y\) 的公共质因子,也即,恰好能被 \(x,y\) 之一整除,那么说明其最小出现次数为 \(0\),只需另插入次小值即可。

这样最多插入 \(7\times 2=14\) 个数,加上最初的两个,是 \(16\) 个,好像还是不太行的亚子呢。因此需要继续优化,也就是说我们要选择合适的 \(x,y\) 使得新加入的元素不超过 \(12\) 个,怎么选呢?我们发现,如果一个质因子在集合中最小出现次数不为 \(0\),那么我们就令 \(x,y\) 为使该质因子出现次数达到最小值和次小值的数即可,这样遍历到该质因子时就不必另外添加其他数,最多就只有 \(12\) 个。否则意味着存在一个数不是 \(2\) 的倍数,也存在一个数不是 \(3\) 的倍数,如果存在一个数同时满足这两个条件,我们就选择这个数作为 \(x\)\(y\) 可以随便选,否则就钦定这两个数为 \(x,y\) 即可。

萌新好像还是第一次见到用 Min-Max 容斥构造的题呢,大佬不喜勿喷(

const int MAXN = 1e5;
const int MAXV = 1e6;
int n, a[MAXN + 5];
int pr[MAXV / 5 + 5], prcnt = 0, vis[MAXV + 5], mnp[MAXV + 5];
void sieve(int n) {
	for (int i = 2; i <= n; i++) {
		if (!vis[i]) pr[++prcnt] = i, mnp[i] = i;
		for (int j = 1; j <= prcnt && pr[j] * i <= n; j++) {
			vis[pr[j] * i] = 1; mnp[pr[j] * i] = pr[j];
			if (i % pr[j] == 0) break;
		}
	}
}
vector<pii> calc(int v) { // (质因子,出现次数) 
	vector<pii> res;
	while (v ^ 1) {
		int p = mnp[v], cnt = 0;
		while (v % p == 0) v /= p, cnt++;
		res.pb(mp(p, cnt));
	}
	return res;
}
bool in[MAXN + 5];
vector<int> occ[MAXV + 5];
pii c[MAXV + 5];
void calc_c() {
	for (int i = 1; i <= n; i++) {
		vector<pii> vec = calc(a[i]);
//		for (pii p : vec) printf("(%d, %d) ", p.fi, p.se);
//		printf("\n");
		for (pii p : vec) occ[p.fi].pb(p.se);
	}
	for (int i = 2; i <= MAXV; i++) if (!vis[i]) {
		if (occ[i].size() <= n - 2) c[i] = mp(0, 0);
		else if (occ[i].size() == n - 1) {
			c[i].se = 0; c[i].fi = 1e9;
			for (int x : occ[i]) chkmin(c[i].fi, x);
		} else {
			c[i].fi = c[i].se = 1e9;
			for (int x : occ[i]) {
				if (x < c[i].fi) c[i].se = c[i].fi, c[i].fi = x;
				else if (x < c[i].se) c[i].se = x;
			}
		}
	}
}
int getcnt(int v, int p) {
	int cnt = 0;
	while (v % p == 0) v /= p, cnt++;
	return cnt;
}
void work(int x, int y) {
	vector<pii> v1 = calc(a[x]), v2 = calc(a[y]);
	in[x] = in[y] = 1;
	for (pii p : v1) {
		if (a[y] % p.fi == 0) {
			vector<pii> vec;
			for (int i = 1; i <= n; i++)
				vec.pb(mp(getcnt(a[i], p.fi), i));
			sort(vec.begin(), vec.end());
			in[vec[0].se] = in[vec[1].se] = 1;
		}
	}
	if (n > 2) {
		for (pii p : v1) {
			if (a[y] % p.fi) {
				vector<pii> vec;
				for (int i = 1; i <= n; i++) if (i != x && i != y)
					vec.pb(mp(getcnt(a[i], p.fi), i));
				sort(vec.begin(), vec.end()); in[vec[0].se] = 1;
			}
		}
		for (pii p : v2) {
			if (a[x] % p.fi) {
				vector<pii> vec;
				for (int i = 1; i <= n; i++) if (i != x && i != y)
					vec.pb(mp(getcnt(a[i], p.fi), i));
				sort(vec.begin(), vec.end()); in[vec[0].se] = 1;
			}
		}
	}
}
void gen_smaller_set() {
	for (int i = 1; i <= MAXV; i++) if (c[i].fi && c[i].se) {
		vector<pii> vec;
		for (int j = 1; j <= n; j++)
			vec.pb(mp(getcnt(a[j], i), j)); 
		sort(vec.begin(), vec.end());
		work(vec[0].se, vec[1].se); return;
	}
	bool flg = 1;
	for (int i = 1; i <= MAXV; i++) flg &= (c[i].fi == 0 && c[i].se == 0);
	if (flg) puts("0"), exit(0);
	for (int i = 1; i <= n; i++) if (a[i] % 2) {
		if (a[i] % 3) {work(i, (i == 1) ? 2 : 1); return;}
		else {
			for (int j = 1; j <= n; j++) if (a[j] % 3) {
				work(i, j); return;
			}
		}
	}
}
int main() {
	scanf("%d", &n); sieve(MAXV);
	for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
	calc_c(); gen_smaller_set();
	vector<int> vec;
	vector<pair<int, vector<int> > > res;
	for (int i = 1; i <= n; i++) if (in[i]) vec.pb(i);
	for (int i = 1; i < (1 << vec.size()); i++) {
		int coef = ((__builtin_popcount(i) & 1) ? (-1) : 1) * (__builtin_popcount(i) - 2);
		vector<int> vecc;
		for (int j = 0; j < vec.size(); j++) if (i >> j & 1) vecc.pb(vec[j]);
		for (int j = 1; j <= abs(coef); j++) res.pb(mp(coef < 0, vecc));
	}
	printf("%d\n", res.size());
	for (int i = 0; i < res.size(); i++) {
		printf("%d %d", res[i].fi, res[i].se.size());
		for (int x : res[i].se) printf(" %d", x);
		printf("\n");
	}
	return 0;
}
posted @ 2022-06-12 20:57  tzc_wk  阅读(84)  评论(0)    收藏  举报