【BZOJ3551】【洛谷P4197】—Peaks加强版(Kruscal重构树+主席树)

BZOJ传送门

洛谷传送门(注意洛谷上并没有要求强制在线)

洛谷 A C AC AC,本机 A C AC AC,然而 B Z O J BZOJ BZOJ T L E TLE TLE

辣鸡 B Z O J BZOJ BZOJ


考虑到有多次询问瓶颈路
我们构建 K r u s c a l Kruscal Kruscal重构树

由于重构树特殊的性质
树上一个点的子树的权值必定都是比这个点小的

那么对于每次询问的 v v v,我们考虑找到它到根路径上第一个权值大于等于 x x x的点
这个可以用树上倍增实现

那现在的问题就变成了在一个点的子树中,查询所有叶子节点的第 k k k
这个对于 d f s dfs dfs序中叶子节点建主席树就解决了

虽然感觉实现有点难写

不过有个小技巧:我们可以发现重构树上每个非叶节点必定有2个儿子

那么我们可以直接对每个点维护左右儿子,就不需要建邻接表

具体细节可以看代码(丑到窒息)

还有重构树可能是不连通的

这个情况要判断

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	char ch=getchar();
	int res=0,f=1;
	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
	while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
	return res*f;
}
const int N=500005;
const int M=1000005;
const int Log=22;
int n,m,q,h[N],s[N],ans=-1;
namespace Kruscal{
	struct edge{
		int u,v,w;
	}e[N];
	int adj[N<<1],nxt[M<<2],to[M<<2],val[N<<1],tot,cnt,lc[N<<1],rc[N<<1],f[N<<1][Log],fa[N];
	inline bool comp(const edge &a,const edge &b){
		return a.w<b.w;
	}
	inline void addedge(int u,int v){
		nxt[++cnt]=adj[u],adj[u]=cnt,to[cnt]=v;
	}
	int find(int x){
		return fa[x]==x?x:fa[x]=find(fa[x]);
	}
	inline void kruscal(){
		sort(e+1,e+m+1,comp);
		for(int i=1;i<=m;i++){
			int f1=find(e[i].u),f2=find(e[i].v);
			if(f1!=f2){
				fa[f1]=fa[f2]=fa[++tot]=tot;
				f[f1][0]=f[f2][0]=tot;
				lc[tot]=f1,rc[tot]=f2;
				val[tot]=e[i].w;
			}
		}
	}
}
namespace JZMS_Tree{
	#define mid ((l+r)>>1)
	#define lc(u) son[u][0]
	#define rc(u) son[u][1]
	int rt[N<<3],sum[7000000],num,son[7000000][2];
	void insert(int &u,int r1,int l,int r,int pos){
		u=++num;
		sum[u]=sum[r1]+1;
		if(l==r)return;
		lc(u)=lc(r1),rc(u)=rc(r1);
		if(pos<=mid)insert(lc(u),lc(r1),l,mid,pos);
		else insert(rc(u),rc(r1),mid+1,r,pos);
	}
	int query(int r1,int r2,int l,int r,int k){
		if(l==r)return s[l];
		int t=sum[rc(r1)]-sum[rc(r2)];
		if(t>=k)return query(rc(r1),rc(r2),mid+1,r,k);
		else return query(lc(r1),lc(r2),l,mid,k-t);
	}
	#undef lc
	#undef rc
}
namespace Pre_operator{
	using namespace Kruscal;
	using namespace JZMS_Tree;
	int in[N<<1],out[N<<1];
	int dfn;
	void dfs(int u){
		for(int i=1;i<=20;i++)
			f[u][i]=f[f[u][i-1]][i-1];
		if(u<=n){
			in[u]=out[u]=++dfn;
			insert(rt[dfn],rt[dfn-1],1,n,h[u]);
			return;
		}
		dfs(lc[u]),dfs(rc[u]);
		in[u]=in[lc[u]],out[u]=out[rc[u]];
	}
	int get(int u,int k){
		for(int i=20;i>=0;i--){
			if(f[u][i]&&val[f[u][i]]<=k)
				u=f[u][i];
		}
		return u;
	}
}
using namespace Pre_operator;
int main(){
	tot=n=read(),m=read(),q=read();
	for(int i=1;i<=n;i++)h[i]=s[i]=read();
	sort(s+1,s+n+1);
	for(int i=1;i<=n;i++)
		h[i]=lower_bound(s+1,s+n+1,h[i])-s;
	for(int i=1;i<=m;i++){
		e[i].u=read(),e[i].v=read(),e[i].w=read();
	}
	for(int i=1;i<=n;i++)fa[i]=i;
	kruscal();
	for(int i=tot;i;i--)
	if(!in[i])dfs(i);
	dfs(find(1));
	for(int i=1;i<=q;i++){
		int v=read(),x=read(),k=read();
		if(ans!=-1)v^=ans,x^=ans,k^=ans;
		v=get(v,x);
		if(out[v]-in[v]+1<k)ans=-1;
		else ans=query(rt[out[v]],rt[in[v]-1],1,n,k);
		cout<<ans<<'\n';
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值