题意
给你n个城市m个边,每条边有一权值,表示路费
如果给出一笔钱,钱大于路费则表示该条路可以通过
通过这条路 则代表 点 (a,b) 和(b,a)是合法的点,合法的对数为2
k次查询,每次查询给出一笔钱,求该笔钱能走过的所有点的合法对数
对输入的边按权值排序,对输入的钱数按权值排序
对第一笔钱,把权值比钱小的边 全都加到一个并查集,
设两端点为t1,t2
int f1=findx(t1); int f2=findx(t2);
那么也就是说,把t1,t2所在的两个联通块合并了,新的根节点为f2,bingcha[f1]=f2;
所以两个联通块新增加的边数,为f1块的点乘上f2快的每一个点,并且由于x,y和y,x算两对点,所以总的为2*fa[f1]*fa[f2]//fa一直维护的是 该联通块的节点的个数
并且累计新增的合法对数 ans+=2*fa[f1]*fa[f2] // f1,f2为两个端点在并查集中的根节点
并且辅助数组fa[f2]+=fa[f1] //必须是f1,f2,不能是t1,t2 此时就维护该新的联通块的节点个数,因为f1块已经被合并到f2块,就没必要维护了(再也不会访问到)
-------
对这笔钱做完操作后记录下ans,转入下一笔钱,由于已经排序了,下笔钱大于等于本笔钱,一定能覆盖 之前算过的点,
所以只需要利用之前的结果,在原来已经添加了的边的基础上直接继续添加边,在原ans上直接累加直到所有边遍历完
最终复杂度O(N)
最后按要求输出答案即可、
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std;
#define inf 0x7fffffff
struct node
{
int l,r,val;
};
node tm[100050];
int cmp(node a,node b)
{
return a.val<b.val;
}
node mm[5005];
int bingcha[20050];
int findx(int x) //x所在集合的根 强哥的
{
if(x==bingcha[x])return x;
return bingcha[x]=findx(bingcha[x]);
}
int ANS[5005];
int fa[20050];
int main()
{
int t;
scanf("%d",&t);
int i,j;
int n,m,q;
while(t--)
{
scanf("%d%d%d",&n,&m,&q);
for( i=1;i<=n;i++) fa[i]=1; //初始化
for( i=1;i<=n;i++) bingcha[i]=i; //初始化
for (i=1;i<=m;i++)
{
scanf("%d%d%d",&tm[i].l,&tm[i].r,&tm[i].val);
}
sort(tm+1,tm+1+m,cmp);
for (i=1;i<=q;i++)
{
scanf("%d",&mm[i].val);
mm[i].l=i;
}
sort(mm+1,mm+1+q,cmp);
int last=1;
int ans=0;
for (i=1;i<=q;i++)
{
int num=mm[i].l;
for (j=last;tm[j].val<=mm[i].val&&j<=m;j++)
{
int t1=tm[j].l;
int t2=tm[j].r;
if (t1==t2 ) continue;
int f1=findx(t1);
int f2=findx(t2);
if (f1==f2) continue;
bingcha[f1]=f2;
ans+=2*fa[f1]*fa[f2];
// int tmp=fa[f1];
fa[f2]+=fa[f1];
// fa[f2]+=tmp;
}
last=j;
ANS[num]=ans;
}
for (i=1;i<=q;i++)
printf("%d\n",ANS[i]);
}
return 0;
}