题目链接
题目大意
给你一棵树,求有多少条简单路径(u,v),满足u到v这条路径上的最大值不超过k。q次查询。(u<v)
题目思路
我还以为是换根dp啥的,没想到居然是类似于kruskal的思想来解题。
将所有边按权值从小到大排序,查询值qi也从小到大排序。对于每次查询的值qi,将边权小于等于qi的边通过并查集合并在一起。对于合并后的联通块,每个联通块对答案的贡献为size×(size−1)2,所有联通块的贡献直接求和即可。所以每次合并两个节点时,先将这两个节点所在的联通块的贡献减去,再加上合并后的贡献即可。
代码
#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define debug printf("I am here\n");
using namespace std;
typedef long long ll;
const int maxn=2e5+5,mod=2147493647,inf=0x3f3f3f3f;
int n,m,fa[maxn],num[maxn];
ll sum,ans[maxn];
pair<int,int> q[maxn];
struct node{
int v,u,w;
}e[maxn];
int findd(int x){
return x==fa[x]?fa[x]:fa[x]=findd(fa[x]);
//最开始写成这样t了,注意还要使fa[x]=findd(fa[x])
//return fa[x]==x?x:findd(fa[x]);
}
void unite(int a,int b){
int faa=findd(a),fab=findd(b);
if(faa==fab) return ;
sum+=1ll*num[faa]*num[fab];
num[faa]+=num[fab];
fa[fab]=faa;
}
bool cmp(node a,node b){
return a.w<b.w;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
fa[i]=i;num[i]=1;
}
for(int i=1;i<=n-1;i++){
scanf("%d%d%d",&e[i].v,&e[i].u,&e[i].w);
}
for(int i=1;i<=m;i++){
scanf("%d",&q[i].fi);
q[i].se=i;
}
sort(e+1,e+1+n-1,cmp);sort(q+1,q+1+m);
int last=1;
for(int i=1;i<=m;i++){
for(int j=last;j<=n-1;j++){
if(e[j].w<=q[i].fi){
unite(e[j].v,e[j].u);
last++;
}else{
break;
}
}
ans[q[i].se]=sum;
}
for(int i=1;i<=m;i++){
printf("%lld%c",ans[i],i==m?'\n':' ');
}
return 0;
}
参考链接:https://www.cnblogs.com/–Simon/p/11479662.html