为了方便,我们仅保留\((v,u,-w)\)的反向边。
可以发现,如果某个点\(u\)到\(v_1,v_2\)同时有相同边权的边,那么\((v_1,v_2)\)就是一个合法的点对。因此可以这样暴力扩展,用bitset优化,能过\(64\)分。
这个方法的问题在于没有用到双向边的性质。具体的,我们发现,如果\((u,v_1)\)是可达的,\((u,v_2)\)是可达的,那么\((v_1,v_2)\)也是可达的,因为路径双向。
则可以用并查集维护这个东西,然后对于每个联通块,统计连到外面的边。这里发现每种颜色的边只需要保留一条,因为之前已经都合并过了。所以用个能合并的东西维护就好了。
因为275307894a很懒,所以写了个启发式合并map,复杂度两个\(\log\),如果写线段树合并可以做到一个\(\log\)。
code:
#include<bits/stdc++.h>
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((m)*(x-1)+(y))
#define R(n) (rnd()%(n))
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using ll=long long;using db=double;using lb=long db;using ui=unsigned;using ull=unsigned ll;
using namespace std;const int N=3e5+5,M=5e5+5,K=1e5+5,mod=998244353,Mod=mod-1,INF=2e9+7;const db eps=1e-7;mt19937 rnd(263082);
int n,m,k,x,y,z,fa[N],Si[N];ll Ans;unordered_map<int,int> f[N];
int GF(int x){return fa[x]^x?fa[x]=GF(fa[x]):x;}queue<pair<int,int>> Q;
#define fi first
#define se second
void Ins(int x,int y,int w){
if(!f[x].count(w)) f[x][w]=y;
else Q.push({y,f[x][w]});
}
int main(){
int i,j;scanf("%d%d%d",&n,&m,&k);for(i=1;i<=m;i++)scanf("%d%d%d",&x,&y,&z),Ins(y,x,z);for(i=1;i<=n;i++) fa[i]=i;
while(!Q.empty()){
auto p=Q.front();Q.pop();if(GF(p.fi)==GF(p.se)) continue;
int x=GF(p.fi),y=GF(p.se);f[x].size()<f[y].size()&&(swap(x,y),0);
for(auto j:f[y]) Ins(x,j.se,j.fi);fa[y]=x;
}
for(i=1;i<=n;i++) Si[GF(i)]++;for(i=1;i<=n;i++) Ans+=1ll*Si[i]*(Si[i]-1)/2;printf("%lld\n",Ans);
}
标签:fa,int,luogu,ll,db,WC2021,P7323,using,define
From: https://www.cnblogs.com/275307894a/p/17056905.html