题目
题意:
给定一棵带权树,有m次访问,每次输入一个qi。要求输出所有的点对(u,v),他们之间的路径最大值小于访问的权值。
分析:
如果每次访问都重新建树的话,显然会爆时间。其实我们可以把访问离线,从小到大排序,再把边从小到大排序。这样我们就可以根据不断的访问逐渐把一棵树建出来。对于一条可以加入的边,它对答案的贡献就是它所连的两个联通块中的点的数量相乘。为了维护连通块中点的数量,我们使用并查集即可。
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
int parent[200005],size[200005];
struct edge{
int x,y,val;
bool operator<(const edge&e)const
{
return val < e.val;
}
}a[200005];
struct query{
int id,val;
bool operator<(const query&q)const
{
return val < q.val;
}
}q[200005];
ll res[200005];
int find(int p)
{
if( p == parent[p] ) return p;
return parent[p] = find(parent[p]);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n,m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
parent[i] = i;
size[i] = 1;
}
for (int i = 1; i < n; i++)
{
cin >> a[i].x >> a[i].y >> a[i].val;
}
sort(a+1,a+n);
for (int i = 1; i <= m; i++)
{
cin >> q[i].val;
q[i].id = i;
}
sort(q+1,q+1+m);
int index = 1;
ll ans = 0;
for (int i = 1; i <= m; i++)
{
while( index < n && a[index].val <= q[i].val )
{
int x = a[index].x;
int y = a[index].y;
int rootx = find(x);
int rooty = find(y);
ans += (ll)size[rootx] * size[rooty];
size[rootx] += size[rooty];
parent[rooty] = rootx;
index ++;
}
res[q[i].id] = ans;
}
for (int i = 1; i <= m; i++)
{
cout << res[i];
if( i == m ) cout << '\n';
else cout << ' ';
}
return 0;
}