CF1305G Kuroni and Antihype
有的人说晚上要爆切2道题,结果以机房太热为由摸鱼,大家快去吊打他
开始以为是和 CF888G 一样的套路,于是打算直接爆切它
但问题在于,前一道题可以先去重再做,而这道题却不行,这样导致 trie树 建出来回产生一系列的问题
然后只能跑去看题解了
用某个 B 开头的 MST 算法肯定是没错了,但我们要跑的是最大生成树
我们定义边权是两端点的点权和,因此最后要减去每个结点的权值;
但还要记得与一个权值为 \(0\) 的虚拟点连边,因为第一个加入的点的权值会被多减一次
考虑怎么快速找到最大值:我们设 \(f[S]\) 为权值是 \(S\) 的子集中最大的点权,这个可以每次跑生成树用子集 DP 进行预处理
但由于我们不能找同一个集合的点,因此我们还有记录一个 \(g[S]\),表示除去 \(f[S]\) 所属的连通块中,权值是 \(S\) 的子集中最大的点权
最后按套路跑生成树即可,别忘了加上自己这个点的点权
代码
#include<iostream>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#define LL long long
#define pii std::pair<int, int>
#define mp std::make_pair
#define FOR(i, x, y) for(int i = (x); i <= (y); i++)
#define ROF(i, x, y) for(int i = (x); i >= (y); i--)
inline int rd()
{
int sign = 1, re = 0; char c = getchar();
while(c < '0' || c > '9'){if(c == '-') sign = -1; c = getchar();}
while('0' <= c && c <= '9'){re = re * 10 + (c - '0'); c = getchar();}
return sign * re;
}
const int MAXN = 1 << 18;
int n, a[200005], fa[200005], kcnt;
int find_fa(int x)
{
if(x == fa[x]) return x;
return fa[x] = find_fa(fa[x]);
}
pii f[1 << 18][2], Mx[200005];
inline void merge(pii *x, pii y)
{
if(y.first == -1) return;
if(x[0].first == -1) return void(x[0] = y);
if(y.first > x[0].first)
{
if(x[0].second != y.second) x[1] = x[0];
return void(x[0] = y);
}
if(y.second != x[0].second && y.first > x[1].first)
return void(x[1] = y);
}
LL ans;
signed main()
{
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
#endif
n = rd();
FOR(i, 1, n) fa[i] = i, a[i] = rd();
kcnt = fa[n + 1] = n + 1;
while(kcnt > 1)
{
FOR(i, 0, MAXN - 1) f[i][0] = f[i][1] = mp(-1, -1);
FOR(i, 1, n + 1)
{
int x = find_fa(i);
merge(f[a[i]], mp(a[i], x));
}
FOR(S, 1, MAXN - 1) FOR(i, 0, 17)
if((S >> i) & 1)
{
int lS = S ^ (1 << i);
merge(f[S], f[lS][0]);
merge(f[S], f[lS][1]);
}
FOR(i, 1, n + 1) Mx[i] = mp(-1, -1);
FOR(i, 1, n + 1)
{
int x = find_fa(i), lS = (MAXN - 1) ^ a[i];
if(f[lS][0].first == -1) continue;
if(f[lS][0].second ^ x && (f[lS][0].first ^ a[i]) > Mx[x].first)
Mx[x] = f[lS][0], Mx[x].first ^= a[i];
else if(f[lS][1].second ^ x && (f[lS][1].first ^ a[i]) > Mx[x].first)
Mx[x] = f[lS][1], Mx[x].first ^= a[i];
}
FOR(i, 1, n + 1) if(i == find_fa(i) && ~Mx[i].first)
{
int x = i, y = find_fa(Mx[i].second);
if(x == y) continue;
fa[x] = y;
kcnt--;
ans += Mx[i].first;
}
}
FOR(i, 1, n) ans -= a[i];
printf("%lld", ans);
return 0;
}