BZOJ 3551: [ONTAK2010]Peaks加强版 【kruskal树+线段树合并】

题目描述:

在Bytemountains有N座山峰,每座山峰有他的高度h_i。有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值c,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰的高度,如果无解输出-1。
N &lt; = 1 0 5 , M , Q &lt; = 5 ∗ 1 0 5 , h i , c , x &lt; = 1 0 9 。 N&lt;=10^5, M,Q&lt;=5*10^5,h_i,c,x&lt;=10^9。 N<=105,M,Q<=5105hi,c,x<=109

题目分析:

原题题面有误,我在上面修改了,原题面没有“的高度”,然后我就WA自闭

加强版是在线的,就先把边排序,把kruskal树中的点每个开一个动态开点的线段树,父亲由两个儿子合并而来。
询问的时候直接从那个点往上倍增找到瓶颈点,在它的线段树中找第k大即可。
(第k高是指有k-1个比它高,所以找的时候要么判右子树的siz,要么在外面把k反过来)

线段树的空间一开始开的maxn*20,结果又RE又WA,开成*30就过了。。。

Code:

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define LL long long
#define maxn 200005
#define maxm 500005
#define maxp maxn*30
using namespace std;
char cb[1<<18],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<18,stdin),cs==ct)?0:*cs++)
inline void read(int &a){
	char c;while(!isdigit(c=getc()));
	for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
int n,m,Q,ans,h[maxn],f[maxn][20],F[maxn];
struct node{
	int x,y,z;
	bool operator < (const node &p)const{return z<p.z;}
}e[maxm];
int find(int x){return x==F[x]?x:F[x]=find(F[x]);}
int search(int x,int v){
	for(int i=17;i>=0;i--) if(h[f[x][i]]<=v) x=f[x][i];
	return x;
}
int rt[maxn],lc[maxp],rc[maxp],s[maxp],id[maxp],tot;
void insert(int &now,int l,int r,int x){
	s[++tot]=s[now]+1,lc[tot]=lc[now],rc[tot]=rc[now];
	now=tot;
	if(l==r) return;
	int mid=(l+r)>>1;
	if(x<=mid) insert(lc[now],l,mid,x);
	else insert(rc[now],mid+1,r,x);
}
void merge(int &now,int x,int y){
	if(!x||!y) now=x+y;
	else{
		s[now=++tot]=s[x]+s[y];
		merge(lc[now],lc[x],lc[y]);
		merge(rc[now],rc[x],rc[y]);
	}
}
int query(int now,int l,int r,int k){
	if(l==r) return l;
	int mid=(l+r)>>1;
	if(k<=s[lc[now]]) return query(lc[now],l,mid,k);
	else return query(rc[now],mid+1,r,k-s[lc[now]]);
}
int main()
{
	#ifndef ONLINE_JUDGE
		freopen("H.in","r",stdin);
	#endif
	int x,y,k;
	read(n),read(m),read(Q);
	for(int i=1;i<=n;i++) read(h[i]),insert(rt[i],1,1e9,h[i]);
	for(int i=1;i<=m;i++) read(e[i].x),read(e[i].y),read(e[i].z);
	sort(e+1,e+1+m);
	for(int i=1;i<=n;i++) F[i]=i;
	for(int i=1;i<=m;i++){
		x=find(e[i].x),y=find(e[i].y);
		if(x!=y){
			++n,F[n]=F[x]=F[y]=f[x][0]=f[y][0]=n;
			h[n]=e[i].z,merge(rt[n],rt[x],rt[y]);
		}
	}
	h[0]=1<<30;
	for(int j=1;j<=17;j++)
		for(int i=1;i<=n;i++)
			f[i][j]=f[f[i][j-1]][j-1];
	while(Q--){
		read(x),read(y),read(k);
		if(ans>0) x^=ans,y^=ans,k^=ans;
		x=search(x,y);
		if(s[rt[x]]<k) printf("%d\n",ans=-1);
		else printf("%d\n",ans=query(rt[x],1,1e9,s[rt[x]]-k+1));
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值