https://codeforces.com/problemset/problem/1213/G
题意: 给你一棵树 然后树的边有权值 每次询问树的路径中最大的权值不超过k的路径数量
思路:如果我们一开始就把树建好 然后每次都去dfs的话 那么肯定是会超时的 那么就考虑将询问离线!!!(常用操作) 对于每一个离线的询问的值W 把小于W的边都加入进来 这个时候是用并查集维护联通快的个数 然后答案就是每个联通快中初始点的个数n * (n - 1) / 2;初始值为0 每次加边改变答案
当两个联通快合并时 需要减去原先两个联通块的贡献 然后在加上新合并联通块的贡献 然后记录答案
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5 + 10;
int pre[maxn];
ll sum[maxn],t_ans[maxn];
struct node{
int u;
int v;
ll w;
}edge[maxn];
struct qqq{
ll val;
int pos;
}query[maxn];
int find(int x)
{
return pre[x] == x ? x : pre[x] = find(pre[x]);
}
bool cmp(node x,node y)
{
return x.w < y.w;
}
bool cmp1(qqq x,qqq y)
{
return x.val < y.val;
}
int main()
{
int n,q;
scanf("%d%d",&n,&q);
for(int i = 1; i <= n - 1; i++)
{
scanf("%d%d%I64d",&edge[i].u,&edge[i].v,&edge[i].w);
}
sort(edge + 1,edge + n, cmp);
for(int i = 1; i <= q; i++)
{
scanf("%I64d",&query[i].val);
query[i].pos = i;
}
sort(query + 1,query + 1 + q, cmp1); // 询问离线
for(int i = 1; i <= n; i++)
{
pre[i] = i;
sum[i] = 1;
}
int len = 1;
ll temp = 0;
for(int i = 1; i <= q; i++)
{
//cout<<query[i].val<<endl;
while(query[i].val >= edge[len].w && len <= n - 1)
{
//cout<<edge[len].w<<endl;
int x = find(edge[len].v), y = find(edge[len].u);
if(x != y) // 更新答案
{
temp -= (sum[x] * (sum[x] - 1) / 2);
temp -= (sum[y] * (sum[y] - 1) / 2);
sum[y] += sum[x];pre[x] = y;
temp += (sum[y] * (sum[y] - 1) / 2);
}
len++;
}
t_ans[query[i].pos] = temp;
}
for(int i = 1; i <= q; i++)
{
cout<<t_ans[i]<<" ";
}
return 0;
}