Problem - 1213G - Codeforces
跟树有关的题目如果涉及到多次询问,考虑并查集来进行动态维护。
题目大意:给定一棵带有边权的树,对于每次询问,会询问一个权值限定 q i q_i qi,你需要找出当前树上满足路径中最大值小于等于 q i q_i qi的路径个数。
解题思路:有多组输入,优先考虑离线做法,因为这是一颗树,我们可以考虑用并查集来进行建树,将每个询问依照权值大小来进行排序,优先处理权值小的询问,边也按照权值大小来进行排序,这样每连接一条边,那么产生的路径数就是这条边的两端点所连接的连通块上点数的乘积。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define syncfalse ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
const int N = 2e5+5;
struct edge{
ll from, to, cost;
bool operator<(edge&a){
return cost < a.cost;
}
}a[N];
struct node{
ll val, id, ans;
}b[N];
bool cmp1(node&a, node&b){
return a.val < b.val;
}
bool cmp2(node&a, node&b){
return a.id < b.id;
}
ll par[N], num[N];
void init(int n){
for (int i = 1; i <= n; ++i)par[i] = i, num[i]=1;
}
int find(int x){
if (x == par[x])return x;
else return par[x] = find(par[x]); //路径压缩
}
int main(){
syncfalse
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int n, m;
cin>>n>>m;
init(n);
for (int i = 1; i < n; ++i){
cin>>a[i].from>>a[i].to>>a[i].cost;
}
sort(a+1, a+n);
for (int i = 1; i <= m; ++i){
cin>>b[i].val;
b[i].id=i;
}
sort(b+1, b+1+m, cmp1);
int l = 1;
ll ans = 0;
for (int i = 1; i <= m; ++i){
bool flag = false;
while(l<n&&a[l].cost<=b[i].val){
int x = a[l].from, y=a[l].to;
x = find(x);
y = find(y);
par[x] = y;
ans+=num[x]*num[y];
num[y]+=num[x]; //合并连通块
num[x]=0;
l++;
flag=true;
}
b[i].ans=ans;
}
sort(b+1, b+1+m, cmp2);
for (int i = 1; i <= m; ++i)cout << b[i].ans << " ";
return 0;
}