题意:
两点之间建立传送门需要的能量为他们之间所有路径里最小的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;
}