题意:
有
m
m
m条边,每个边都有一个权值
w
i
w_i
wi
有
q
q
q次询问,对于每个询问也有一个权值
q
i
q_i
qi
辉夜在第
i
i
i次询问时只能通过
w
i
<
=
q
i
w_i<=q_i
wi<=qi的边
对每次询问有多少个点对可以满足让辉夜从
a
a
a出发走到
b
b
b
分析:
很眼熟的题好吧
但就是不知道为什么没有做题记录和博客
回归正题,我们可以对所有边和询问的权值升序排序,面临一个新的询问时可以控制一个指针加入满足条件的边,然后答案则可以用并查集来判断是否在同一连通块,若不在那该边会产生的贡献就是两个连通块大小的乘积
时间复杂度为
O
(
q
+
m
)
O(q+m)
O(q+m)
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<queue>
#define LL long long
using namespace std;
inline LL read()
{
LL s=0,f=1; char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') {s=s*10+c-'0';c=getchar();}
return s*f;
}
struct node{
LL x,y,d;
}e[100005];
struct rnm{
LL w,k;
}x[5005];
bool cmp(node a,node b) {return a.d<b.d;}
bool cmpp(rnm a,rnm b) {return a.w<b.w;}
LL f[20005],size[20005];
LL find(LL i) {return f[i]==i?i:f[i]=find(f[i]);}
LL ans[5005];
int main()
{
LL t=read();
while(t--)
{
LL n=read(),m=read(),q=read();
for(LL i=1;i<=n;i++) f[i]=i,size[i]=1;
for(LL i=1;i<=m;i++) e[i].x=read(),e[i].y=read(),e[i].d=read();
sort(e+1,e+1+m,cmp);
for(LL i=1;i<=q;i++) x[i].w=read(),x[i].k=i;
sort(x+1,x+1+q,cmpp);
LL k=1;
memset(ans,0,sizeof(ans));
for(LL i=1;i<=q;i++)
{
ans[x[i].k]=ans[x[i-1].k];
while(k<=m&&e[k].d<=x[i].w)
{
LL a=find(e[k].y),b=find(e[k].x);
if(a==b) {k++;continue;}
f[a]=b;
ans[x[i].k]+=size[a]*size[b];
size[b]+=size[a];
k++;
}
}
for(LL i=1;i<=q;i++) printf("%lld\n",ans[i]*2);
}
return 0;
}