BZOJ4298 [ONTAK2015] Bajtocja

BZOJ4298 [ONTAK2015] Bajtocja

考虑给每层的每个连通块都赋予随机权值 \(key\)。记 \(val(i)\) 表示点 \(i\) 在每一层中所在连通块的权值 \(key\) 的异或和。

转化一下条件:如果 \(u,v\) 在每一层中都连通,说明每一层中 \(u,v\) 都处在相同的连通块内。

有了随机权值这一工具,上述条件可以进一步转化为 \(val(u)=val(v)\)。可以使用系统自带的哈希表维护 \(val\)

如果需要合并两个集合,则启发式合并,维护并查集的同时维护以 \(u\) 为首的连通块内具体有哪些点,用 vector 存储。并入的结点更换了连通块,需要同时修改 \(val\) 和哈希表。

如何统计答案呢?考虑在哈希表插入或删除时同步维护,注意点对是有序的。

// Title:  Bajtocja
// Source: BZOJ4298
// Author: Jerrywang
#include <bits/stdc++.h>
#define rep(i, s, t) for(int i=s; i<=t; ++i)
#define F first
#define S second
#define pii pair<int, int>
#define ll long long
#define debug(x) cout<<#x<<":"<<x<<endl;
const int N=5005, M=205;
using namespace std;

mt19937 rnd(time(0));
int d, n, T, ans; ll val[N];
unordered_map<ll, int> mp;

struct layer
{
    int f[N]; ll key[N];
    vector<int> a[N];
    void init()
    {
        rep(i, 1, n) f[i]=i, key[i]=rnd()*rnd(), a[i].push_back(i);
    }
    int fa(int u)
    {
        if(u==f[u]) return u;
        return f[u]=fa(f[u]);
    }
    void merge(int u, int v)
    {
        u=fa(u), v=fa(v);
        if(u==v) return;
        if(a[u].size()<a[v].size()) swap(u, v);
        f[v]=u;
        for(int x:a[v])
        {
            a[u].push_back(x);
            ll &t=val[x];
            ans-=(--mp[t])*2;
            val[x]=val[x]^key[x]^key[u];
            ans+=(mp[t]++)*2;
            key[x]=key[u];
        }
        a[v].clear();
    }
} a[M];

int main()
{
    scanf("%d%d%d", &d, &n, &T); ans=n;
    rep(i, 1, d) a[i].init();
    rep(i, 1, n)
    {
        rep(j, 1, d) val[i]^=a[j].key[i];
        mp[val[i]]++;
    }
    while(T--)
    {
        int u, v, k; scanf("%d%d%d", &u, &v, &k);
        a[k].merge(u, v);
        printf("%d\n", ans);
    }

    return 0;
}
posted @ 2023-11-26 16:36  JosephusWang  阅读(14)  评论(0)    收藏  举报