洛谷 - P4197 Peaks(Kruskal重构树+dfs序+主席树)

题目链接:点击查看

题目大意:有 n 座山峰,每座山峰有他的高度 h[ i ] ,有些山峰之间有双向道路相连,共 m 条路径,每条路径有一个困难值,这个值越大表示越难走。

现在有 q 组询问,每组询问询问从点 v 开始只经过困难值小于等于 x 的路径所能到达的山峰中第 k 高的山峰,如果无解输出 −1。

题目分析:因为有困难值的限制,所以可以对整个图跑克鲁斯卡尔重构树,如果对点 v 来说,只能走小于等于 x 的路径,可以树上倍增找到权值小于等于 x 的,深度最浅的祖先,显然这个祖先子树中的所有点都是可以从点 v 到达的,接下来找第 k 小,就是主席树的工作了,dfs 序上建一下主席树就好了

注意,题目中需要求的是第 k 大,而不是第 k 小

代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
using namespace std;
 
typedef long long LL;
 
typedef unsigned long long ull;
 
const int inf=0x3f3f3f3f;
 
const int N=1e6+100;

int h[N],f[N],val[N],dp[N][25],L[N],R[N],n,nn,m,q,tot,limit;

bool vis[N];
/*主席树*/ 
struct Node
{
	int l,r;
	int sum;
}tree[N*20];

int cnt,root[N];
 
void init()
{
	root[0]=0;
	tree[0].l=tree[0].r=tree[0].sum=0;
	cnt=1;
}
 
void update(int num,int &k,int l,int r)
{
	tree[cnt++]=tree[k];
	k=cnt-1;
	tree[k].sum++;
	if(l==r)
		return;
	int mid=l+r>>1;
	if(num<=mid)
		update(num,tree[k].l,l,mid);
	else
		update(num,tree[k].r,mid+1,r);
}
 
int query(int i,int j,int k,int l,int r)//i:左端点根节点编号,j:右端点根节点编号,k:第k大的数,l,r:区间[l,r]
{
	int d=tree[tree[j].r].sum-tree[tree[i].r].sum;
	if(l==r)
		return l;
	int mid=l+r>>1;
	if(k<=d)
		return query(tree[i].r,tree[j].r,k,mid+1,r);
	else
		return query(tree[i].l,tree[j].l,k-d,l,mid);
}
/*主席树*/ 
struct Edge
{
	int u,v,w;
	bool operator<(const Edge& t)const
	{
		return w<t.w;
	}
}edge[N];

vector<int>node[N];//邻接表 
/*dfs序+树上倍增*/
void dfs(int u,int fa)
{
	vis[u]=true;
	dp[u][0]=fa;
	for(int i=1;i<=limit;i++)
		dp[u][i]=dp[dp[u][i-1]][i-1];
	L[u]=++tot;
	root[tot]=root[tot-1];
	if(u<=n)
		update(h[u],root[tot],1,nn);
	for(auto v:node[u])
		dfs(v,u);
	R[u]=tot;
}
/*dfs序+树上倍增*/
/*并查集+克鲁斯卡尔重构树*/
int find(int x)
{
	return f[x]==x?x:f[x]=find(f[x]);
}

void Ex_Kruskal()
{
	int index=n;
	for(int i=1;i<=n<<1;i++)
		f[i]=i;
	sort(edge+1,edge+1+m);
	for(int i=1;i<=m;i++)
	{
		int xx=find(edge[i].u),yy=find(edge[i].v);
		if(xx!=yy)
		{
			f[xx]=f[yy]=++index;
			val[index]=edge[i].w;
			node[index].push_back(xx);
			node[index].push_back(yy);
		}
	}
}
/*并查集+克鲁斯卡尔重构树*/
/*离散化*/
vector<int>disc;

void discreate()
{
	sort(disc.begin(),disc.end());
	disc.erase(unique(disc.begin(),disc.end()),disc.end());
	nn=disc.size();
	for(int i=1;i<=n;i++)
		h[i]=lower_bound(disc.begin(),disc.end(),h[i])-disc.begin()+1;
}
/*离散化*/
int get_pos(int u,int up)//树上倍增找满足的祖先 
{
	for(int i=20;i>=0;i--)
		if(dp[u][i]!=0&&val[dp[u][i]]<=up)
			u=dp[u][i];
	return u;
}

int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	init();
	scanf("%d%d%d",&n,&m,&q);
	limit=log2(n)+1;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",h+i);
		disc.push_back(h[i]);
	}
	discreate();
	for(int i=1;i<=m;i++)
		scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
	Ex_Kruskal();
	for(int i=1;i<=n;i++)
		if(!vis[i])
			dfs(find(i),0);
	while(q--)
	{
		int v,x,k;
		scanf("%d%d%d",&v,&x,&k);
		v=get_pos(v,x);
		if(tree[root[R[v]]].sum-tree[root[L[v]-1]].sum<k)
			puts("-1");
		else
			printf("%d\n",disc[query(root[L[v]-1],root[R[v]],k,1,nn)-1]);
	}








   return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/108187207