Path Queries 解题报告

题目链接:http://codeforces.com/problemset/problem/1213/G

You are given a weighted tree consisting of n vertices. Recall that a tree is a connected graph without cycles. Vertices ui and vi are connected by an edge with weight wi .

You are given m queries. The i -th query is given as an integer qiqi . In this query you need to calculate the number of pairs of vertices (u,v) (u

Input

The first line of the input contains two integers n and m (1≤n,m≤2⋅10^5) — the number of vertices in the tree and the number of queries.

Each of the next n−1 lines describes an edge of the tree. Edge i is denoted by three integers ui , vi and wi — the labels of vertices it connects (1≤ui,vi≤n,ui≠vi) and the weight of the edge (1≤wi≤2⋅10^5). It is guaranteed that the given edges form a tree.

The last line of the input contains mm integers q1,q2,…,qm (1≤qi≤2⋅10^5 ), where qi is the maximum weight of an edge in the i -th query.

Output

Print m integers — the answers to the queries. The i -th value should be equal to the number of pairs of vertices (u,v) (u

 

题目大意:给出一棵含有n个结点的树和连接这棵树的各边权值,并给出m个询问,每个询问中有一个整数q_i;对于每个询问,求满足连接两点的路径上不出现边权大于q_i的边的点对数。

首先我们可以从题目中看出:对于每个询问,边权大于其给定的数字q_i的边均为无效边,即没有任何路径能从该边上通过;因此,任何一条无效边能将一棵树割成两棵树,这两棵树之间的点不能配对。

把排除无效边后每一棵孤立的树看成一个连通块,则每个连通块的点可以相互连通,然后可以用到一个组合数学常识:一个连通块中可以形成\frac{size\times (size-1)}{2}个点对(不考虑顺序),其中size为连通块大小,即连通块中点的个数。

(这里有两种证明方法,初中同学如果不了解这个公式可以看一下:1、可以利用等差数列,例如6个点,那么第一个点可以和其它5个点配对,第二个点和除了第一个点的其它4个点配对,第三个点和除了第一、第二个点的其它3个点配对……以此类推,则点对数为5+4+3+2+1=\frac{6\times 5}{2},即\frac{size\times (size-1)}{2};2、利用排列组合知识解决,这相当于size个点中取两个点构成点对,即C_{size}^2=\frac{size*(size-1)}{2}

于是我们可以考虑两种思路:1、删边;2、加边。

这都离不开对询问进行离线处理,否则我们每次都得将树恢复或清空重新删边加边,极大地提高时间复杂度;那么,我们以删边为例,将询问和各边从大到小排序。

于是对于某个询问,我们可以直接将当前未删去但是大于q_i的边都依次删除,在删除时,连通块一分为二,所以答案应该在先前基础上减去原连通块的贡献,然后再加上两个新连通块的贡献。

然而实际操作会发现,我们很难维护连通块的大小size,因为每一次删边,会影响从该边向上整条链的子结点个数,而子结点是用来存储连通块大小的重要依据。

因此我们换种思路,考虑加边。

加边则与删边相反,它是连通块由多变少的过程,因此我们将边和询问按从小到大排序,然后依次加边,由于原有连通块不会分裂,因此只需要维护每个连通块的根储存的子结点数目并作为连通块大小(开始全部为1),查询时用并查集即可;原理则只是将加边的思路倒过来:在原答案的基础上,减去原来两个小连通块对答案的贡献并加上新的大连通块的贡献。

代码:

#include
#include
using namespace std;
	struct newdata
	{
		int v,label;
	};
	struct doubly
	{
		int u,v,w;
	};
	int n,m,cnt;
	int fa[200001];
	long long sum[200001];
	long long ans[200001];
	newdata query[200001];
	doubly path[200001];
bool cmp(newdata i,newdata j)
{
	return i.v

 

你可能感兴趣的:(树,并查集,组合数学)