CF1687E Become Big For Me 【min-max 容斥,构造】

给定 \(n\) 个正整数 \(a_1,\cdots,a_n\),构造 \(k_1+k_2\le 10^5\) 个子序列 \(b_1,\cdots,b_{k_1},c_1,\cdots,c_{k_2}\) 满足:

  • \(\sum|b_i|+\sum|c_i|\le 10^6\)
  • \(\prod \text{lcm}(b_i)/\prod \text{lcm}(c_i)=\gcd_{i\ne j}\{a_ia_j\}\)

\(n\le 10^5\)\(a_i\le 10^6\),保证有解。


由 min-max 容斥知 \(\gcd_{i\ne j}\{a_ia_j\}=\prod_{\varnothing\ne T\subset S}\operatorname{lcm}(T)^{(-1)^{|T|}(|T|-2)}\),问题转化为选择尽可能少的一些数 \(c_1,\cdots,c_k\) 使得 \(\gcd_{i\ne j}\{a_ia_j\}=\gcd_{i\ne j}\{c_ic_j\}\)

先考虑要求 \(\gcd\{a_i\}=\gcd\{c_i\}\) 怎么办,任取一个元素 \(x\)从小到大枚举 \(x\) 的质因子 \(p\),把 \(\nu_p(a_i)\) 取到最小的 \(a_i\) 加入,注意如果 \(\nu_p\) 已经取到最小就不用加了,选了至多 \(\omega(x)+1=8\) 个数,实际上若选了 \(8\) 个数则可以把 \(x\) 扔掉,否则第一次选的 \(a_i\) 缺一个最小质因子,多一个别的质因子,这就爆值域了。

设这样构造的子集是 \(g(a)\),则对原问题用 \(g(a)\cup g(a\backslash g(a))\) 即可。

#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
using namespace std;
typedef pair<int, int> pii;
template<typename T>
bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
const int N = 100003, M = 1000003;
int n, a[N], pri[N], mp[M], tot;
bitset<M> notp;
vector<pii> vc[N];
vector<int> pos, res;
vector<pair<vector<int>, bool> > ans;
int qry(int i, int p){
	auto it = lower_bound(vc[i].begin(), vc[i].end(), MP(p, 0));
	return it != vc[i].end() && it -> fi == p ? it -> se : 0;
}
void work(){
	if(pos.empty()) return;
	vector<int> del;
	for(auto [p, c] : vc[pos[0]]){
		int nx = 0, now = c;
		for(int i = 0;i < del.size();++ i) chmin(now, qry(pos[del[i]], p));
		for(int i = 1;i < (int)pos.size() && now;++ i)
			if(chmin(now, qry(pos[i], p))) nx = i;
		if(nx) del.push_back(nx);
	}
	sort(del.begin(), del.end());
	del.erase(unique(del.begin(), del.end()), del.end());
	if(del.size() < 7) del.insert(del.begin(), 0);
	for(int i = (int)del.size() - 1;i >= 0;-- i){
		res.push_back(pos[del[i]]);
		pos.erase(pos.begin() + del[i]);
	}
}
int main(){
	ios::sync_with_stdio(0);
	notp.set(0); notp.set(1);
	for(int i = 2;i < M;++ i){
		if(!notp.test(i)){mp[i] = i; pri[tot++] = i;}
		for(int j = 0;j < tot && i * pri[j] < M;++ j){
			notp.set(i * pri[j]);
			mp[i * pri[j]] = pri[j];
			if(!(i % pri[j])) break;
		}
	}
	cin >> n;
	for(int i = 0;i < n;++ i){
		cin >> a[i]; int x = a[i];
		while(x > 1){
			int p = mp[x], c = 0;
			while(!(x % p)){x /= p; ++ c;}
			vc[i].emplace_back(p, c);
		}
	}
	pos.resize(n);
	iota(pos.begin(), pos.end(), 0);
	work(); work();
	int m = res.size();
	for(int S = 1;S < (1 << m);++ S){
		int pc = __builtin_popcount(S), ct = (pc & 1) ? 2 - pc : pc - 2;
		bool flg = ct < 0; if(flg) ct = -ct;
		vector<int> hah;
		for(int i = 0;i < m;++ i) if(S >> i & 1) hah.push_back(res[i]);
		sort(hah.begin(), hah.end());
		while(ct --) ans.emplace_back(hah, flg);
	}
	printf("%d\n", (int)ans.size());
	for(auto [hah, flg] : ans){
		printf("%d %d", flg, (int)hah.size());
		for(int i : hah) printf(" %d", i + 1);
		putchar('\n');
	}
}
posted @ 2022-06-07 20:08  mizu164  阅读(92)  评论(0编辑  收藏  举报