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