G. Path Queries

题目

题意:

    给定一棵带权树,有m次访问,每次输入一个qi。要求输出所有的点对(u,v),他们之间的路径最大值小于访问的权值。
     1 n , m 2 1 0 5 , 1 q i 2 1 0 5 1≤n,m≤2⋅10^5,1≤q_i≤2⋅10^5

分析:

    如果每次访问都重新建树的话,显然会爆时间。其实我们可以把访问离线,从小到大排序,再把边从小到大排序。这样我们就可以根据不断的访问逐渐把一棵树建出来。对于一条可以加入的边,它对答案的贡献就是它所连的两个联通块中的点的数量相乘。为了维护连通块中点的数量,我们使用并查集即可。

#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;
}

发布了132 篇原创文章 · 获赞 6 · 访问量 7929

猜你喜欢

转载自blog.csdn.net/weixin_44316314/article/details/104855048