「BZOJ3545」「ONTAK2010」 Peaks - Splay启发式合并

题目描述

在Bytemountains有N座山峰,每座山峰有它的高度h_i。有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1。

输入格式

第一行三个数N,M,Q。

第二行N个数,第i个数为h_i。

接下来M行,每行3个数a b c,表示从a到b有一条困难值为c的双向路径。

接下来Q行,每行三个数v x k,表示一组询问。

输出格式

对于每组询问,输出一个整数表示所求山峰的高度。

数据范围

N<=105, M,Q<=5*105,hi,c,x<=109

分析

由于题目没有要求要在线,于是考虑离线。读入所有的边和询问,将边和询问分别按照 c c x x 从小到大排序。然后对每个点开 N N S p l a y Splay ,对于每个询问,将所有 c x c\le x 的边的两顶点的 S p l a y Splay 合并起来,然后在 v v 所在的 S p l a y Splay 中查询第 K K 大。由于先排序了的,所以顺序处理每个询问,在上一个询问的基础上合并。

代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N=100005,M=500005;
typedef long long LL;
struct Edge {
	int x,y,w;
	bool operator<(const Edge&e) const {
		return w<e.w;
	}
}e[M];
struct Query {
	int v,x,k,id;
	bool operator<(const Query&e) const {
		return x<e.x;
	}
}q[M];
int n,m,Q,sz[N];
int v[N],f[N],c[N][2],ans[M];
int fa[N],pt[N],st[N],top;
void Pushup(int x) {if (x) sz[x]=sz[c[x][0]]+sz[c[x][1]]+1;}
int Getf(int x) {return fa[x]==x?x:fa[x]=Getf(fa[x]);}//用并查集判断联通性 
void Rotate(int x) {
	int y=f[x],z=f[y];
	int k=c[y][1]==x,kk=c[z][1]==y;
	c[y][k]=c[x][k^1];
	f[c[x][k^1]]=y;
	c[x][k^1]=y;
	f[y]=x;
	if (z) c[z][kk]=x;
	f[x]=z;
	Pushup(y);
	Pushup(x);
}
void Splay(int x) {
	while (f[x]) {
		int y=f[x],z=f[y];
		if (z) {
			if ((c[y][1]==x)^(c[z][1]==y)) Rotate(x);
			else Rotate(y);
		}
		Rotate(x);
	}
}
void Insert(int p,int q) {
	while (1) {
		sz[p]++;
		int nxt=(v[q]<=v[p]?0:1);
		if (!c[p][nxt]) {
			c[p][nxt]=q;
			f[q]=p;
			c[q][0]=c[q][1]=0;
			sz[q]=1;
			Splay(q);
			return;
		}
		p=c[p][nxt];
	}
}//以上Splay基本操作
void Dfs(int x) {
	if (!x) return;
	Dfs(c[x][0]);
	st[++top]=x;
	Dfs(c[x][1]);
}
void Merge(int x,int y) {
	int fx=Getf(x);
	int fy=Getf(y);
	if (fx==fy) return;
	fa[fy]=fx;
	Splay(x);
	Splay(y);
	if (sz[x]<sz[y]) swap(x,y);//启发式,将小的向大的合并 
	Dfs(y);//获得中序遍历序,据说可以减少时间复杂度
	st[top+1]=x;
	while (top) {
		Insert(st[top+1],st[top]);//一个个插入
		top--;
	}
}
int Kth(int p,int k) {
	Splay(p);
	if (k>sz[p]) return -1;
	k=sz[p]-k+1;
	while (1) {
		if (sz[c[p][0]]+1==k) return v[p];
		if (sz[c[p][0]]>=k) p=c[p][0];
		else {
			k-=sz[c[p][0]]+1;
			p=c[p][1];
		}
	}
}
int main() {
	scanf("%d%d%d",&n,&m,&Q);
	for (int i=1;i<=n;i++) scanf("%d",&v[i]),fa[i]=i,sz[i]=1;//读入并初始化
	for (int i=1;i<=m;i++) scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].w);
	for (int i=1;i<=Q;i++) scanf("%d%d%d",&q[i].v,&q[i].x,&q[i].k),q[i].id=i;
	sort(e+1,e+m+1);
	sort(q+1,q+Q+1);
	int ei,qi;
	ei=qi=1;
	while (qi<=Q) {
		while (ei<=m&&e[ei].w<=q[qi].x) {
			Merge(e[ei].x,e[ei].y);
			ei++;
		}
		ans[q[qi].id]=Kth(q[qi].v,q[qi].k);
		qi++;
	}
	for (int i=1;i<=Q;i++) printf("%d\n",ans[i]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/sin_Yang/article/details/85491058