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');
}
}