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;
}
posted @ 2022-09-17 08:26  zuytong  阅读(22)  评论(0编辑  收藏  举报