题意:给出一个带权非联通图,图中有若干联通分量,图中的点代表城市,有一些城市可以互相到达。试去掉边权小于x的所有边,试问去掉这些边后,还有多少对城市可以互相到达?
并查集+离散化
思路:
可以先对每个连通分量进行预处理,即保留权值尽可能大的边使得连通分量保持原有连通性,对于连通分量中一些无用(权值较小)的边直接删除。优化连通分量的具体操作是对按照权值进行排序,优先选用权值较大的边,这样总能生成一颗更健壮的树。建树的同时记录每种权值的边所能连接的点对数,这样破坏某些边时就能快速获取这些边能影响的点对数。最后对记录做一下前缀预处理,方便统计某种前缀。
场上写的时候,数组初始化没整好,WA了好几次。
code:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10 ;
struct Node{
int x , y , w;
bool operator < (Node no1) const{
return w > no1.w ;
}
};
vector<int>S;
set<int>st ;
int t , n , m , Q ;
int q[N] ;
int p[N] ;
Node Eg[N] ;
int cnt[N] ;
int Cc[N*2];
int All = 0 ;
int get(int x)
{
return lower_bound(S.begin(),S.end(),x) - S.begin() + 1;
}
int fr(int x)
{
return x == p[x] ? p[x] : p[x] = fr(p[x]) ;
}
void merge(int x , int y , int w)
{
x = fr(x) , y = fr(y) ;
if(x == y) return ;
Cc[w] += cnt[x] * cnt[y] ;
All = All + cnt[x] * cnt[y];
p[x] = y ;
cnt[y] += cnt[x];
}
signed main()
{
//freopen("in","r",stdin);
scanf("%lld",&t);
while (t--){
S.clear() ;
st.clear() ;
All = 0 ;
for(int i = 0 ; i < N ; i++){
p[i] = i ;
cnt[i] = 1 ;
// Cc[i] = 0 ;
}
memset(Cc,0,sizeof Cc);
scanf("%lld%lld%lld",&n,&m,&Q) ;
for(int i = 1 ; i <= m ; i++) {
scanf("%lld%lld%lld",&Eg[i].x,&Eg[i].y,&Eg[i].w);
st.insert(Eg[i].w) ;
}
for(int i = 1 ; i <= Q ; i++) scanf("%lld",&q[i]) , st.insert(q[i]) ;
for(auto it : st) S.push_back(it);
for(int i = 1 ; i <= m ; i++) {
Eg[i].w = get(Eg[i].w);
}
for(int i = 1 ; i <= Q ; i++) q[i] = get(q[i]) ;
sort(Eg+1,Eg+1+m);
for(int i = 1 ; i <= m ; i++) {
merge(Eg[i].x , Eg[i].y , Eg[i].w) ;
}
for(int i = 1 ; i < 2*N ; i++) {
Cc[i] = Cc[i] + Cc[i-1] ;
}
for(int i = 1 ; i <= Q ; i++){
//cout << q[i] << "***" << endl ;
printf("%lld\n",All - Cc[q[i]-1]);
}
}
return 0 ;
}