虽然不难,但是感觉很有意思的一道题,下面有几个点感觉还是挺妙的:
· 并查集只能加边不能减边;
·对数据进行离线处理,方便我们进行排序处理(后面会解释);
首先数据的范围很大,如果我们每做一次判断都要进行一次遍历的话时间复杂度肯定会超时,所以我们想能不能像预处理那样只需要做一次就能得到所有我们想要的答案呢?
首先我们先对所有边进行一个从大到小的排序,所以我们在判断毁掉了多少条路的时候只需要判断当前边的值是否小于毁灭的值即可,如果不能毁掉那么后面的也无法破坏,break就好。
那么还是没有解决 O(t * m * q)这样一个时间复杂度,接下来我们对查询的值也进行一个从大到小的排序(注意我们输出的时候还是要按原顺序输出,这个地方定义一个id编号即可),排序之后刚开始我们从大到小进行找,因为查询值大的无法破坏的边,后面的小的查询值也无法破坏,所以我们换个思路,题目给的是破坏的多少条边,反过来经过排序之后逆向思维可以理解成查询的值变小了又加入了多少条边,这样我们就可以用并查集来进行处理了。
如果一个集合里面有n个元素,那么就有1+2+3+……+n-1个城市对。为了避免每次做完查询操作都要对集合进行一个遍历,所以我们用一个ans来记录目前为止符合条件的城市对的数量,遇到可以有新加入的点我们把对应的值加进去。但是两个集合合并的话不能进行一个简单的相加,因为会有重复,意思是我们之前这两个集合各自的城市对已经加入ans了,合并之后又加一遍会有数据重复,所以这里处理可以有两种做法,一种是先减去两个集合各自城市对的数量,然后合并之后再加上合并之后的城市对的数量,另一种做法是对前一种做法的一个整理,假设第一个集合里面有n个城市,第二个集合里面有m个城市,所以第一个集合的城市对有n*(n-1)/2,第二个集合的城市对有m*(m-1)/2,合并之后总的城市对有 (n+m) * (n+m-1)/2,整理后为 (n+m) * (n+m-1)/2 - n*(n-1)/2 - m*(m-1)/2 = n*m,所以我们合并两个集合的时候加n * m 也可以。
完整代码如下:
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
const int maxn = 2*1e5+100;
struct infor{
int x,y;
int cost;
}e[maxn];
struct lian{
long long sum;
int f;
}fa[maxn];
struct qs{
long long zhi;
int id;
}se[maxn];
bool cmp1(infor a,infor b){
return a.cost > b.cost;
}
bool cmpc(qs a,qs b){
return a.zhi > b.zhi;
}
int find(int x){
if(x==fa[x].f) return x;
else fa[x].f = find(fa[x].f);
return fa[x].f;
}
void solve(){
long long jie[maxn];
int n,m,q;
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++){
fa[i].sum = 1;
fa[i].f = i;
}
for(int i=0;i<m;i++){
scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].cost);
}
for(int i=0;i<q;i++){
scanf("%lld",&se[i].zhi);
se[i].id = i;
}
sort(e,e+m,cmp1);
sort(se,se+q,cmpc);
int j=0;
long long ans = 0;
for(int i=0;i<q;i++){
for(j;j<m;j++){
int from = e[j].x;
int to = e[j].y;
int c = e[j].cost;
if(c<se[i].zhi) break;
else{
from = find(from);
to = find(to);
if(from!=to){
long long a,b;
a = fa[from].sum;
b = fa[to].sum;
fa[from].f = to;
fa[to].sum += fa[from].sum;
ans += fa[to].sum*(fa[to].sum-1)/2 - a*(a-1)/2 - b*(b-1)/2;
}
}
}
jie[se[i].id] = ans;
}
for(int i=0;i<q;i++){
printf("%lld\n",jie[i]);
}
}
int main(){
int t;
cin >> t;
while(t--){
solve();
}
return 0;
}