P8026 [ONTAK2015] Bajtocja

题目大意

\(d\) 张图,\(n\) 个点,\(m\) 次操作,每一次操作在第 \(k\) 张图上在 \((u,v)\) 之间连一条无向边,求每一次操作后的所有满足在 \(d\) 张图上全部联通的点对数量。
\(d\leq200,n\leq5\times10^3,m\leq 10^6\)

思路

对于本题,发现只要一个点对 \((u,v)\) 在每一个图中都被加入到了同一个集合里即为合法。则直接考虑维护 \(d\) 个并查集。所以设 \(f_{u,i}\) 表示 \(u\) 在第 \(i\) 个图中所属的集合,则只要所有的 \(f_{u,i},i\in[1,d]\) 全部相等即可。所以我们维护一个哈希表示 \(u\) 的所有图的所属集合,之后查询这个哈希有多少个,则就是满足条件,若大小为 \(s\) 则可以为最终答案贡献 \(s^2\)

之后维护并查集需要更改哈希,如果是路径压缩不好处理,考虑启发式合并即可。

发现边数虽然看起来很大但是能更改的边数只有最多 \(nd\) 条,所以如果操作的 \((a,b)\) 在第 \(k\) 张图上已经在同一个集合内答案不会改变。

最终时间复杂度 \(O(nd\log n)\)

代码

#include <bits/stdc++.h>
#define endl "\n"
using namespace std;
typedef long long ll;
const ll MAXN=5e3+5;
const ll MAXD=200+5;
ll n,d,q;
ll fa[MAXN][MAXD];
vector<ll>se[MAXN][MAXD];
ll ans;
ll ha[MAXN],base[MAXN];
unordered_map<ll,ll>val;
void change(ll x,ll op){
    ll mx=val[ha[x]];
    ans-=mx*mx;
    val[ha[x]]+=op;
    mx=val[ha[x]];
    ans+=mx*mx;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin>>d>>n>>q;
    for(int i=1;i<=d;++i){
        base[i]=rand();
    }
    for(int i=1;i<=n;++i){
        for(int j=1;j<=d;++j){
            fa[i][j]=i;
            se[i][j].push_back(i);
            ha[i]+=base[j]*i;
        }
        change(i,1);
    }
    while(q--){
        ll u,v,k;
        cin>>u>>v>>k;
        u=fa[u][k],v=fa[v][k];
        if(u==v){
            cout<<ans<<endl;
            continue;
        }
        if(se[u][k].size()>se[v][k].size()){
            swap(u,v);
        }
        for(auto x:se[u][k]){
            fa[x][k]=v;
            se[v][k].push_back(x);
            change(x,-1);
            ha[x]-=base[k]*u;
            ha[x]+=base[k]*v;
            change(x,1);
        }
        se[u][k].clear();
        cout<<ans<<endl;
    }
    return 0;
}