洛谷P4197 Peaks

题目大意

n n n座山峰,每座山峰有他的高度 h [ i ] h[i] h[i]。有些山峰之间有双向道路相连,共m条路径,每条路径有一个困难值,这个值越大表示越难走。现在有 q q q组询问,每组询问询问从点 v v v开始只经过困难值小于等于 x x x的路径所能到达的山峰中第 k k k高的山峰,如果无解输出 − 1 −1 1

分析

  • 对题目进行分析,看到"从点 v v v开始只经过困难值小于等于 x x x的路径"这句话时想到了最近一直在练的 K r u s k a l Kruskal Kruskal重构树,还有由于每次询问的是山峰中第 k k k高的山峰,所以不难想到运用主席树。
  • 首先当然是先做一遍普通的重构树,然后跑一边 d f s dfs dfs。重构树中每一个非叶子节点对应的一定是一段连续的区间,所以开一个 r a n g e [ x ] [ 0 / 1 ] range[x][0/1] range[x][0/1]数组,表示 x x x节点的所维护的最左的叶子节点和最右的叶子节点,那么我们就可以比较清晰地做主席树了。在做 d f s dfs dfs时每跑到一个叶子节点就加到主席树中,对于每一组询问,先用倍增求出你所能到达的深度最小的点,然后用该节点的 r a n g e [ x ] [ 0 ] range[x][0] range[x][0] r a n g e [ x ] [ 1 ] range[x][1] range[x][1]做为两个历史版本做主席树的查询操作即可。

Code

#pragma GCC optimize(3)
#include <cstdio>
#include <algorithm>
using namespace std;

const int N = 2e5 + 10;
const int M = 5e5 + 10;
struct Node{
	int from,to,val;
} g[M];
struct Edge{
	int to,next;
} f[M << 1];
int n,m,q,fa[N],h[N][25],v[N],cnt,tot,head[N],rt[N << 5],ls[N << 5],rs[N << 5],a[N],b[N],sz,num,range[N][2],sum[N << 5];

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

bool cmp(Node a,Node b) {return a.val < b.val;}
int find(int a) {return a == fa[a] ? a : fa[a] = find(fa[a]);}

void add(int u,int v) 
{
	f[++ cnt].to = v;
	f[cnt].next = head[u];
	head[u] = cnt;
}

void kruskal()
{
	for (int i = 1; i <= 2 * n; i ++) fa[i] = i;
	sort(g + 1,g + 1 + m,cmp);
	tot = n;
	for (int i = 1; i <= n; i ++)
	{
		int x = find(g[i].from),y = find(g[i].to);
		if (x == y) continue;
		fa[x] = fa[y] = ++ tot,v[tot] = g[i].val;
		add(tot,x),add(tot,y);
		h[x][0] = h[y][0] = tot;
	}
}

void build(int &x,int l,int r)
{
	x = ++ cnt;
	if (l == r) return;
	int mid = l + r >> 1;
	build(ls[x],l,mid),build(rs[x],mid + 1,r);
}

void modify(int pre,int &rt,int l,int r,int k)
{
	rt = ++ cnt;
	sum[rt] = sum[pre] + 1;
	if (l == r) return;
	int mid = l + r >> 1;
	if (k <= mid) rs[rt] = rs[pre],modify(ls[pre],ls[rt],l,mid,k); else ls[rt] = ls[pre],modify(rs[pre],rs[rt],mid + 1,r,k);
}

int query(int x,int y,int l,int r,int k)
{
	if (l == r) return l;
	int mid = l + r >> 1,q = sum[rs[y]] - sum[rs[x]];
	if (k <= q) return query(rs[x],rs[y],mid + 1,r,k); else return query(ls[x],ls[y],l,mid,k - q);
}

void dfs(int u)
{
	for (int i = 1; i <= 20; i ++) h[u][i] = h[h[u][i - 1]][i - 1];
	range[u][0] = num;
	if (!head[u])
	{
		int x = lower_bound(b + 1,b + 1 + sz,a[u]) - b;
		range[u][0] = ++ num;
		modify(rt[num - 1],rt[num],1,sz,x);
		return;
	}
	for (int i = head[u]; i; i = f[i].next)
		dfs(f[i].to);
	range[u][1] = num;
}

int main()
{
	n = read(),m = read(),q = read();
	for (int i = 1; i <= n; i ++) a[i] = b[i] = read();
	sort(b + 1,b + 1 + n);
	sz = unique(b + 1,b + 1 + n) - b - 1;
	for (int i = 1; i <= m; i ++)
		g[i].from = read(),g[i].to = read(),g[i].val = read();
	kruskal();
	cnt = 0;
	build(rt[0],1,sz);
	dfs(tot);
	while (q --)
	{
		int x = read(),y = read(),z = read();
		for (int i = 20; i >= 0; i --)
			if (h[x][i] && v[h[x][i]] <= y) x = h[x][i];
		if (range[x][1] - range[x][0] < z) printf("%d\n",-1); 
		else printf("%d\n",b[query(rt[range[x][0]],rt[range[x][1]],1,sz,z)]);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值