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