Bzoj3551:[ONTAK2010]Peaks加强版:Kruskal+主席树

题目链接:[ONTAK2010]Peaks加强版

做一遍Kruscal,对于要合并联通块的两个点x,y新建节点z令fa[x]=fa[y]=z,并且节点z的权值为这条边的边权

那么我们对于一个询问<v,x,k>只需要倍增出最后一个权值>x的节点,这颗子树就是我们要找到的联通块

主席树维护即可

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=300010;
const int maxN=5000000;
const int inf=0x7fffffff/2-1;
int n,m,f[maxn],tmp[maxn],a[maxn];
struct edge{int a,b,w;}e[maxn<<1];
int lson[maxN],rson[maxN],T[maxn];
int tot=0,cnt=0,N=0,totr=0,ind=0;
int s[maxN],fa[maxn][20],st[maxn];
int h[maxn],ed[maxn],dep[maxn],Q;
struct edges{int to,next;}G[maxn<<2];

int read(){
    int x=0; char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x;
}

int getpos(int x){
	int l=1,r=N,ret=-1;
	while (l<=r){
		int mid=(l+r)>>1;
		if (tmp[mid]>x) r=mid-1;
		else l=mid+1,ret=mid;
	}return ret;
}

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

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

void add(int x,int y){
	G[++tot].to=y;G[tot].next=h[x];h[x]=tot;
}

void ins(int pre,int &rt,int l,int r,int pos){
	rt=++totr; s[rt]=s[pre]+1;
	if (l==r) return;
	int mid=(l+r)>>1;
	if (pos<=mid) rson[rt]=rson[pre],ins(lson[pre],lson[rt],l,mid,pos);
	else lson[rt]=lson[pre],ins(rson[pre],rson[rt],mid+1,r,pos);
}

void dfs(int x){
	dep[x]=dep[fa[x][0]]+1;
	st[x]=++ind;
	for (int i=1;i<=18;++i){
		if (dep[x]<(1<<i)) break;
		fa[x][i]=fa[fa[x][i-1]][i-1];
	}
	if (x<=n) ins(T[ind-1],T[ind],1,N,a[x]);
	else T[ind]=T[ind-1];
	for (int i=h[x];i;i=G[i].next){
		int v=G[i].to;
		fa[v][0]=x; dfs(v);
	}
	ed[x]=ind;
}

int query(int pre,int rt,int l,int r,int k){
	if (l==r) return tmp[l];
	int mid=(l+r)>>1,sum=s[rson[rt]]-s[rson[pre]];
	if (sum>=k) return query(rson[pre],rson[rt],mid+1,r,k);
	else return query(lson[pre],lson[rt],l,mid,k-sum);
}

int main(){
	n=read(); m=read(); Q=read(); cnt=n;
	for (int i=1;i<=n;++i) a[i]=read(),tmp[i]=a[i];
	sort(tmp+1,tmp+n+1); tmp[0]=-inf;
	for (int i=1;i<=n;++i) if (tmp[i]!=tmp[i-1]) tmp[++N]=tmp[i];
	for (int i=1;i<=n;++i) a[i]=getpos(a[i]);
	for (int i=1;i<=m;++i)
		e[i].a=read(),e[i].b=read(),e[i].w=read();
	sort(e+1,e+m+1,cmp); 
	for (int i=1;i<n<<1;++i) f[i]=i;
	for (int i=1;i<=m;++i){
		int x=find(e[i].a),y=find(e[i].b);
		if (x!=y){
			f[x]=f[y]=++cnt;
			add(cnt,x); add(cnt,y);
			a[cnt]=e[i].w;
			if (cnt==(n<<1)-1) break;
		}
	}
	for (int i=1;i<=n;++i){
		int x=find(i);
		if (!st[x]) dfs(x);
	}
	int lastans=0; a[0]=inf;
 	for (int i=1;i<=Q;++i){
		int x=read()^lastans,y=read()^lastans,z=read()^lastans;
		int rt=x;
		for (int j=18;j>=0;--j){
			if (dep[rt]<(1<<j)) continue;
			if (a[fa[rt][j]]<=y) rt=fa[rt][j];
		}
		if (s[T[ed[rt]]]-s[T[st[rt]-1]]<z) lastans=-1;
		else lastans=query(T[st[rt]-1],T[ed[rt]],1,N,z);
		printf("%d\n",lastans);
		lastans=lastans<0?0:lastans;
	}
}


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值