G. Path Queries(带权并查集+kruskal思想)

https://codeforces.com/problemset/problem/1213/G


题意翻译

题目描述

\mathsf E \color{red}\mathsf{ntropyIncreaser}EntropyIncreaser 有一棵 nn 个点的树,每条边都带权。

她会问你 mm 个问题,每次给你一个正整数 qq,求最大权值不大于 qq 的简单路径数量。

需要注意的是,对于一个点对 (u,v)(u,v) 只记一次,单独一个点不算路径。

输入格式

第一行两个正整数 n,mn,m,意义如题目描述。
接下来 n-1n−1 行,每行三个正整数 u,v,wu,v,w,表示 u,vu,v 之间有一条权为 ww 的无向边。
最后一行 mm 个正整数,表示询问。

输出格式

对于每个询问,输出一行一个整数表示答案。

数据范围

1\le n,m \le 2\times10^51≤n,m≤2×105
1\le u,v \le n1≤u,v≤n
1\le w,q \le 2\times 10^51≤w,q≤2×105

输入输出样例

输入 #1复制

7 5
1 2 1
3 2 3
2 4 1
4 5 2
5 7 4
3 6 2
5 2 3 4 1

输出 #1复制

21 7 15 21 3 

输入 #2复制

1 2
1 2

输出 #2复制

0 0 

输入 #3复制

3 3
1 2 1
2 3 2
1 3 2

输出 #3复制

1 3 3 

说明/提示

The picture shows the tree from the first example: 

G. Path Queries(带权并查集+kruskal思想)_第1张图片


开始觉得不好处理,感觉是个有点组合数学算的。

思路:类似最小生成树维护。离线维护。

从小到大排序边和提问,当提问的值>=当前边权值,就把当前边的两个点连起来。所以会对应两个连通块的总和贡献。

当a和b合并的时候,记录siz[a]为a中连通块点数量,siz[b]为b中连通块点数量,那么点对数为siz[a]*siz[b],合并完后更新siz,令a为b的父亲节点,此时新的合成的连通块的点数量为siz[a]+=siz[b],siz[b]=0;

启发:询问可以离线维护。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#define debug(a) cout<<#a<<"="<>n>>m;
  for(LL i=1;i<=n;i++) fa[i]=i,siz[i]=1;
  for(LL i=1;i>edges[i].x>>edges[i].y>>edges[i].w;
  for(LL i=1;i<=m;i++) cin>>ques[i].p,ques[i].id=i;
  sort(edges+1,edges+1+n,cmp1);
  sort(ques+1,ques+1+m,cmp2);
  LL ans=0;LL last=1;
  for(LL i=1;i<=m;i++){
  	for(LL j=last;j<=n;j++){
  		if(edges[j].w<=ques[i].p)
		{
  			LL x=find(edges[j].x);LL y=find(edges[j].y);
			if(x==y) continue;
			ans+=siz[x]*siz[y];
			siz[x]+=siz[y];siz[y]=0;
			fa[y]=x;
			last++;	
		}
		else{
			break;	
		}
	}
	c[ques[i].id]=ans;
  }
  for(LL i=1;i<=m;i++){
  	cout<

 

你可能感兴趣的:(思维,最小生成树,并查集)