Bzoj3545:[ONTAK2010]Peaks:Splay启发式合并

4 篇文章 0 订阅

题目链接:[ONTAK2010]Peaks

离线,将边按照边权从小到大排序,询问按照x从小到大排序

对于每个询问,将边权小于他的x的边加入图中,用splay维护每个联通块的权值,查找第k大即可

加入一条边时会合并联通块,这个用Splay的启发式合并

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=500010;
int n,m,f[maxn],Q,qu[maxn],ans[maxn];
struct edge{int a,b,w;}e[maxn];
struct ques{int v,x,k,id;}q[maxn];
struct Nodes{
	int c[2],fa,v,s;
};
struct Splay_tree{
	Nodes t[maxn];
	void init(){
		for (int i=1;i<=n;++i) scanf("%d",&t[i].v),t[i].s=1,f[i]=i;
	}
	void push_up(int x){
		if (!x) return;
		t[x].s=t[t[x].c[0]].s+t[t[x].c[1]].s+1;
	}
	void rotate(int p,int x){
		int mark= p==t[x].c[1];
		int y=t[p].c[mark^1],z=t[x].fa;
		if (t[z].c[0]==x) t[z].c[0]=p;
		if (t[z].c[1]==x) t[z].c[1]=p;
		if (y) t[y].fa=x; t[x].c[mark]=y;
		t[p].fa=z; t[p].c[mark^1]=x; t[x].fa=p;
		push_up(x);
	}
	void splay(int p,int k){
		while (t[p].fa!=k){
			int x=t[p].fa,y=t[x].fa;
			if (y==k) rotate(p,x);
			else if (p==t[x].c[0]^x==t[y].c[0]) rotate(p,x),rotate(p,y);
			else rotate(x,y),rotate(p,x);
		}push_up(p);
	}
	void ins(int &x,int anc,int now){
		if (!x){
			x=now; t[x].fa=anc;
			t[x].s=1; splay(x,0);
			return;
		}
		if (t[now].v<=t[x].v) ins(t[x].c[0],x,now);
		else ins(t[x].c[1],x,now);
		push_up(x);
	}
	void merge(int x,int y){
		splay(x,0); splay(y,0);
		if (t[x].s>t[y].s) swap(x,y);
		int head=0,tail=1; qu[0]=y; qu[1]=x;
		while (head<tail){
			int u=qu[++head];
			if (t[u].c[0]) qu[++tail]=t[u].c[0];
			if (t[u].c[1]) qu[++tail]=t[u].c[1];
			t[u].c[0]=t[u].c[1]=0;
			ins(qu[head-1],0,u);
		}
	}
	int qkth(int x,int k){
		if (t[x].s<k) return -1;
		if (t[t[x].c[1]].s>=k) return qkth(t[x].c[1],k);
		else if (t[t[x].c[1]].s+1==k) return t[x].v;
		else return qkth(t[x].c[0],k-t[t[x].c[1]].s-1);
	}
	void debug(int x){
		if (t[x].c[0]) debug(t[x].c[0]);
		printf("%d ",t[x].v);
		if (t[x].c[1]) debug(t[x].c[1]);
	}
}s;

bool cmp(const edge &a,const edge &b){
	return a.w<b.w;
}

bool cmp2(const ques &a,const ques &b){
	return a.x<b.x;
}

int find(int x){
	return x==f[x]?x:f[x]=find(f[x]);
}

int main(){
	scanf("%d%d%d",&n,&m,&Q);
	s.init();
	for (int i=1;i<=m;++i)
		scanf("%d%d%d",&e[i].a,&e[i].b,&e[i].w);
	sort(e+1,e+m+1,cmp);
	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(q+1,q+Q+1,cmp2);
	int pos=1;
	for (int i=1;i<=Q;++i){
		while (pos<=m&&e[pos].w<=q[i].x){
			int a=find(e[pos].a),b=find(e[pos].b);
			if (a!=b)s.merge(e[pos].a,e[pos].b),f[a]=b;
			pos++;
		}
		int rt=find(q[i].v);
		s.splay(rt,0);
		ans[q[i].id]=s.qkth(rt,q[i].k);
	}
	for (int i=1;i<=Q;++i) printf("%d\n",ans[i]);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值