Peaks/Peaks加强版[BZOJ3545/3551][ONTAK2010][倍增,Kruskal重构树,主席树]

题目

在这里插入图片描述
在这里插入图片描述

强制在线

思路

Kruskal重构树的性质:

  • 是一个二叉树
  • 如果是按最小生成树建立的话是一个大根堆
  • 任意两个点路径上边权的最大值为它们的 L C A LCA LCA 的点权

这样,我们发现从圆点到根路径上的点的权值是递增的,就可以二分出小于等于 x x x 深度最浅的,然后同一子树内 d f n dfn dfn 序是连续的,我们就能想办法做第 k k k 大查询
我们只需要保留圆点的 d f n dfn dfn 序,因为方点我们只需要权值。
然后就套路了,主席树第 k k k 大,而且还是序列版本。。。

代码

#include<set>
#include<map>
#include<cmath>
#include<deque>
#include<stack>
#include<ctime>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<climits>
#include<iostream>
#include<algorithm>
#define LL long long
#define ULL unsigned long long
using namespace std;
int read(){
	int f=1,x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c=='-')f=-1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return f*x;
}
#define MAXN 100000
#define MAXM 500000
#define INF 0x3f3f3f3f
struct Edge1{
	int v,nxt;
}edge[MAXN+MAXM+5];
int ecnt,head[MAXN+MAXM+5];
inline void Addedge(int u,int v){
	//printf("<%d %d>\n",u,v);
	edge[++ecnt]=(Edge1){v,head[u]},head[u]=ecnt;
	return ;
}
struct Edge2{
	int u,v,w;
	friend bool operator < (Edge2 a,Edge2 b){return a.w<b.w; }
}E[MAXM+5];
int n,lg2,m,q,cnt,dfn_cnt;
int h[MAXN+5],ffa[MAXN+MAXM+5],val[MAXN+MAXM+5];
int fa[MAXN+MAXM+5][21],dfn[MAXN+MAXM+5],tid[MAXN+MAXM+5],Le[MAXN+MAXM+5],Ri[MAXN+MAXM+5];
int Find(int x){return (ffa[x]==x?x:(ffa[x]=Find(ffa[x])));}
void DFS(int u){
	if(u<=n){
		dfn[u]=++dfn_cnt,tid[dfn_cnt]=u,Le[u]=Ri[u]=dfn[u];
		return ;
	}
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].v;
		if(v==fa[u][0]) continue;
		fa[v][0]=u,DFS(v);
		Le[u]=min(Le[u],Le[v]),Ri[u]=max(Ri[u],Ri[v]);
	}
	return ;
}
void Prepare(){
	for(int j=1;j<=20;j++)
		for(int i=1;i<=cnt;i++)
			fa[i][j]=fa[fa[i][j-1]][j-1];
	return ;
}
int rt[MAXN+5],ncnt,ch[30*MAXN+5][2],siz[30*MAXN+5];
void Copy(int u,int pre){
	ch[u][0]=ch[pre][0],ch[u][1]=ch[pre][1],siz[u]=siz[pre];
	return ;
}
void Insert(int &u,int pre,int L,int R,int val){
	u=++ncnt,Copy(u,pre),siz[u]++;
	if(L==R) return ;
	int Mid=(L+R)>>1;
	if(val<=Mid) Insert(ch[u][0],ch[pre][0],L,Mid,val);
	else Insert(ch[u][1],ch[pre][1],Mid+1,R,val);
	return ;
}
int Query_Kth(int u,int v,int L,int R,int k){
	if(L==R) return L;
	int Mid=(L+R)>>1,lsiz=siz[ch[v][0]]-siz[ch[u][0]];
	if(k<=lsiz) return Query_Kth(ch[u][0],ch[v][0],L,Mid,k);
	else return Query_Kth(ch[u][1],ch[v][1],Mid+1,R,k-lsiz);
}
int dcnt,D[MAXN+5];
void Discrete(){
	sort(D+1,D+dcnt+1);
	dcnt=unique(D+1,D+dcnt+1)-(D+1);
	return ;
}
int Query(int u,int x,int k){
	for(int j=20;j>=0;j--)
		if(fa[u][j]&&val[fa[u][j]]<=x)
			u=fa[u][j];
	if(Ri[u]-Le[u]+1<k)
		return -1;
	k=Ri[u]-Le[u]+1-k+1;
	return D[Query_Kth(rt[Le[u]-1],rt[Ri[u]],1,dcnt,k)];
}
int main(){
	n=read(),m=read(),q=read(),lg2=(int)log2(n);
	for(int i=1;i<=n;i++)
		D[++dcnt]=h[i]=read();
	Discrete();
	for(int i=1;i<=n;i++)
		h[i]=lower_bound(D+1,D+dcnt+1,h[i])-D;
	for(int i=1;i<=m;i++){
		int u=read(),v=read(),w=read();
		E[i]=(Edge2){u,v,w};
	}
	cnt=n,sort(E+1,E+m+1);
	for(int i=1;i<=n;i++)
		ffa[i]=i;
	val[0]=INF;
	for(int i=1;i<=m;i++){
		int fu=Find(E[i].u),fv=Find(E[i].v);
		if(fu==fv) continue;
		val[++cnt]=E[i].w;
		ffa[fu]=ffa[fv]=ffa[cnt]=cnt;
		Addedge(cnt,fu),Addedge(cnt,fv);
	}
	memset(Le,63,sizeof(Le));
	for(int i=1;i<=cnt;i++)
		if(ffa[i]==i)
			DFS(i);
	//for(int i=n+1;i<=cnt;i++)
	//	printf("[%d %d]\n",i,val[i]);
	//for(int i=1;i<=n;i++)
	//	printf("{%d %d}\n",i,dfn[i]);
	Prepare();
	for(int i=1;i<=n;i++)
		Insert(rt[i],rt[i-1],1,dcnt,h[tid[i]]);
	int lastans=0;
	for(int i=1;i<=q;i++){
		int v=read(),x=read(),k=read();
		if(lastans!=-1) v^=lastans,x^=lastans,k^=lastans;
		lastans=Query(v,x,k);
		printf("%d\n",lastans);
	}
	return 0;
}

思考

注意第 k k k 大和第 k k k
k k k 大:从大到小第 k k k
k k k 小:从小到大第 k k k
还有

void Prepare(){
	for(int j=1;j<=20;j++)
		for(int i=1;i<=cnt;i++)//不是n!!!
			fa[i][j]=fa[fa[i][j-1]][j-1];
	return ;
}

血的教训。。。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值