Peaks加强版 黑暗爆炸 - 3551 Kruskal重构树 + 主席树

本文介绍了如何利用Kruskal算法重构图并维护连通性,结合主席树解决在给定路径限制下,从特定起点能到达的山峰中第k大的高度查询问题。通过边的权重排序,建立重构树,并使用主席树进行区间查询,高效地解决了大规模数据下的复杂查询。
摘要由CSDN通过智能技术生成

传送门

文章目录

题意:

给你一张图,有 n n n个山峰,每个山峰高度为 h i h_i hi,有 m m m条边,每条边有个难度值 w i w_i wi,现在有 q q q个询问,每次询问给定一个山峰 v v v,问从这个山峰开始走,经过难度不超过 x x x的路径能走到的山峰中,第 k k k大的山峰高度是多少。
n ≤ 1 e 5 , m , q ≤ 5 e 5 , h i , w i < = 1 e 9 n\le1e5,m,q\le5e5,h_i,w_i<=1e9 n1e5,m,q5e5,hi,wi<=1e9

思路:

K r u s k a l Kruskal Kruskal重构树维护连通性经典题啦,把边从小到大排序,让后建重构树,这样每两个点之间的最大路径是他们的 l c a lca lca的点权,那么我们从 v v v这个点往上跳,一直跳到深度最小的且 v a l f ≤ x val_f\le x valfx的点,让后这颗子树中所有叶子节点就是能到的山峰,找第 k k k大的问题当然是留给主席树解决啦,按照 d f s dfs dfs序建主席树,查询第 k k k大就好了。

//#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
//#pragma GCC optimize(2)
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#include<ctime>
#include<cstdlib>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define pb push_back
#define mk make_pair
#define Mid (tr[u].l+tr[u].r>>1)
#define Len(u) (tr[u].r-tr[u].l+1)
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define db puts("---")
using namespace std;

//void rd_cre() { freopen("d://dp//data.txt","w",stdout); srand(time(NULL)); }
//void rd_ac() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//AC.txt","w",stdout); }
//void rd_wa() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//WA.txt","w",stdout); }

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;

const int N=200010,mod=1e9+7,INF=0x3f3f3f3f;
const double eps=1e-6;

int n,m,q;
int a[N],p[N],fa[N][22],in[N],ed[N],se[N],tot;
int root[N],idx,h[N];
vector<int>v[N],li;
struct Edge
{
	int a,b,w;
	bool operator < (const Edge &W) const
	{
		return w<W.w;
	}
}edge[N*3];
struct Node
{
	int l,r;
	int cnt;
}tr[N*40];

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

int get(int x)
{
	return lower_bound(li.begin(),li.end(),x)-li.begin();
}

void insert(int p,int &q,int l,int r,int pos)
{
	q=++idx; tr[q]=tr[p];
	tr[q].cnt++;
	if(l==r) return;
	int mid=(l+r)>>1;
	if(pos<=mid) insert(tr[p].l,tr[q].l,l,mid,pos);
	else insert(tr[p].r,tr[q].r,mid+1,r,pos);
}

int query(int p,int q,int l,int r,int k)
{
	if(l==r) return li[l];
	int mid=(l+r)>>1,cnt=tr[tr[q].l].cnt-tr[tr[p].l].cnt;
	if(k<=cnt) return query(tr[p].l,tr[q].l,l,mid,k);
	else return query(tr[p].r,tr[q].r,mid+1,r,k-cnt);
}

void dfs(int u,int f)
{
	se[u]=1;
	in[u]=++tot; fa[u][0]=f;
	for(int i=1;i<=19;i++) fa[u][i]=fa[fa[u][i-1]][i-1];
	if(u<=n) insert(root[tot-1],root[tot],0,li.size()-1,get(a[u]));
	else root[tot]=root[tot-1];
	for(auto x:v[u]) if(x!=f) dfs(x,u),se[u]+=se[x];
	ed[u]=tot;
}

int main()
{
//	ios::sync_with_stdio(false);
//	cin.tie(0);

	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),li.pb(a[i]);
	sort(li.begin(),li.end()); li.erase(unique(li.begin(),li.end()),li.end());
	for(int i=1;i<=n*2;i++) p[i]=i;
	for(int i=1;i<=m;i++)
	{
		int a,b,w; scanf("%d%d%d",&a,&b,&w);
		edge[i]={a,b,w};
	}
	sort(edge+1,edge+1+m);
	for(int i=1,tot=n;i<=m;i++)
	{
		int aa=edge[i].a,b=edge[i].b,w=edge[i].w;
		int pa=find(aa),pb=find(b);
		if(pa==pb) continue;
		tot++; p[pa]=tot; p[pb]=tot;
		v[tot].pb(pa); v[tot].pb(pb);
		a[tot]=w;
		if(tot==n*2-1) break;
		//cout<<pa<<' '<<pb<<' '<<tot<<' '<<w<<endl;
	}
	a[0]=INF;
	for(int i=1;i<=n*2-1;i++) if(i==find(i)) dfs(i,0);
	int ans=0;
	while(q--)
	{
		int v,x,k; scanf("%d%d%d",&v,&x,&k);
		v^=ans; x^=ans; k^=ans; 
		for(int i=19;i>=0;i--) if(a[fa[v][i]]<=x) v=fa[v][i];
		int sum=tr[root[ed[v]]].cnt-tr[root[in[v]-1]].cnt;
		if(k>sum) ans=-1;
		else ans=query(root[in[v]-1],root[ed[v]],0,li.size()-1,sum-k+1);
		printf("%d\n",ans);
		ans=ans==-1? 0:ans;
	}
	
	

	return 0;
}
/*

*/


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值