HDU - 3938 Portal (并查集,)

题意:

两点之间建立传送门需要的能量为他们之间所有路径里最小的T,一条路径的T为该路径上最长的边的长度。现在 Q 个询问,问 L 能量可以选择多少种不同点对?

思路:

因为有Q 个询问,我们可以离线做所有的询问,然后最后输出答案。

我们可以对所有的权值排序,从小到大,然后进行并查集,如果两个点不在一个并查集,那么两个点加在一起,然后他们可以产生点对是 两个并查集中点的个数的乘积。

之所以从小到大排序,是因为使用小的能量建立传送门,L 能量变大也一定可以建立传送门,所以答案累加就行了。

我们把两个并查集连在一起的时候的那个权值,就是必走的路径。 两个并查集的点对建立传送门所需的能量就是连起来的权值。

这个是求图中的不同的点对,满足最长边小于k。并查集

还有求图中不同的点对,满足路径小于k。 树上点分治。

还有一个题是求删去两条边,(原图是一课树,新加边,一条是树上的边,一条是新加的边)把图分成两部分。 LCA

#include <bits/stdc++.h>
#define mem(x,v) memset(x,v,sizeof(x)) 
#define go(i,a,b)  for (int i = a; i <= b; i++)
#define og(i,a,b)  for (int i = a; i >= b; i--)
using namespace std;
typedef long long LL;
const double EPS = 1e-10;
const int INF = 0x3f3f3f3f;
const int N = 1e5+10;
struct node{
	int val,id;
	bool operator < (node const a)const {
		return val < a.val;
	}
}p[N];
struct edge{
	int x,y,val;
	bool operator <(edge const a) const{
		return val < a.val;
	}
}f[N];
int sum[N],fv[N],fa[N],ans,t,n,m,k;

int find(int k){
	if (k == fa[k]) return k; else return fa[k] = find(fa[k]);
}
void slove(){
	go(i,1,m){
		int fx,fy;
		fx = find(f[i].x); fy = find(f[i].y);
		if (fx != fy){
			ans += fv[fx] * fv[fy];
			fa[fx] = fy;
			fv[fy] += fv[fx];
			while(p[t].val < f[i].val){
				t++; sum[p[t].id] = sum[p[t-1].id];
			}
			sum[p[t].id] = ans; //这个地方注意一下。就好。
		}
	}
	while(t < k) sum[p[++t].id] = ans;
}



int main(){
	while(scanf("%d%d%d",&n,&m,&k) == 3){
		go(i,1,m) scanf("%d%d%d",&f[i].x,&f[i].y,&f[i].val);
		sort(f+1, f+1+m);
		go(i,1,k) scanf("%d",&p[i].val),p[i].id = i;
		sort(p+1,p+1+k);
		mem(sum,0);
		go(i,0,n) fa[i] = i,fv[i] = 1;
		ans = 0; t = 1; p[0].id = 0;
		slove();
		go(i,1,k) printf("%d\n",sum[i]);
	}


	return 0;
}

猜你喜欢

转载自blog.csdn.net/kidsummer/article/details/81711002