[ONTAK2010]Peaks

[ONTAK2010]Peaks
Time Limit: 10 Sec Memory Limit: 128 MB
在Bytemountains有N座山峰,每座山峰有他的高度h_i。有些山峰之间有双向道路相连,共M条路径,
每条路径有一个困难值,这个值越大表示越难走,
现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,
如果无解输出-1。

Input

第一行三个数N,M,Q。
第二行N个数,第i个数为h_i
接下来M行,每行3个数a b c,表示从a到b有一条困难值为c的双向路径。
接下来Q行,每行三个数v x k,表示一组询问。
N<=10^5, M,Q<=5*10^5,h_i,c,x<=10^9。

Output
对于每组询问,输出一个整数表示答案。

Sample Input
10 11 4
1 2 3 4 5 6 7 8 9 10
1 4 4
2 5 3
9 8 2
7 8 10
7 1 4
6 7 1
6 4 8
2 1 5
10 8 10
3 4 7
3 4 6
1 5 2
1 5 6
1 5 8
8 9 2

Sample Output
6
1
-1
8

/*
Sol:此题方法有很多,可以线段树合并,可以重构树+主席树+Dfs.
针对原图做个重构树,大根堆的。
新建出来的点的权值为其所连的两个点的从前的边权即困难值 
于是我们可以从一个点向上跳,只要所跳的点困难值<=所给的值,就可以跳.
跳到指定点后,它所在的树所包括的所有结点,我们就可以求第K大了。
这个用dfs序搞出来就好了。 
*/
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define N 200005
#define M 500005
struct node{int x, y, v;}S[M];
struct node2{int l, r, v;}A[N * 20];
bool cmp(node a, node b){return a.v < b.v;}
int num, tot, res, n, m, t, h[N], bel[N], T[N];
int f[N][25], dep[N], val[N], out[N], ins[N], hgt[N];
int tar[M], nex[M], fir[N], cnt;
void Read(int &p)
{
	p = 0;
	char c = getchar();
	for (; c < '0' || c > '9'; c = getchar());
	for (; c >= '0' && c <= '9'; c = getchar())p = p * 10 + c - '0';
}
void Add(int x, int y)
{
	++cnt;
	tar[cnt] = y;
	nex[cnt] = fir[x];
	fir[x] = cnt;
}
int Getbel(int x)
{
	if (!bel[x])
		return x;
	return bel[x] = Getbel(bel[x]);
}
int Query(int x, int v)
{
	for (int i = 20; i >= 0; i--)
		if (f[x][i] && val[f[x][i]] <= v)
		//只要所跳的点小于等于v时就可以跳到这个点 
			x = f[x][i];
	return x;
}
void Insert(int &q, int l, int r, int v)
{
	A[++num] = A[q];
	q = num;
	A[q].v++;
	if (l == r)
		return;
	int mid = (l + r) >> 1;
	if (v <= mid)
		Insert(A[q].l, l, mid, v);
	else
		Insert(A[q].r, mid + 1, r, v);
}
int Getans(int x, int k, int l, int r, int v)
//x开始时间
//k结束时间
//l,r左右边界
//找第v大 
{
	if (l == r)
		return l;
	int mid = (l + r) >> 1, pos = A[A[k].l].v - A[A[x].l].v;
	if (v <= pos)
		return Getans(A[x].l, A[k].l, l, mid, v);
	return Getans(A[x].r, A[k].r, mid + 1, r, v - pos);
}
void Dfs(int x)
{
	if (x <= n)
		h[++tot] = hgt[x];
	ins[x] = tot;//dfs序, ins是x的进入时间 
	for (int i = 1; i <= 20; i++)
		f[x][i] = f[f[x][i - 1]][i - 1];
	for (int i = fir[x]; i; i = nex[i])
	{
		int v = tar[i];
		f[v][0] = x;
		dep[v] = dep[x] + 1;
		Dfs(v);
	}
	out[x] = tot;//out是x的出去时间 
}
void Build()
{
	res = n;//总结点个数 
	sort(S + 1, S + m + 1, cmp);//边权排序 
	for (int i = 1; i <= m; i++)
	{
        int u = Getbel(S[i].x), v = Getbel(S[i].y);
        //并查集找出父亲点 
        if (u != v)
		{
			val[++res] = S[i].v;
			bel[u] = bel[v] = res;
			Add(res, u), Add(res, v);
		}
	}
}
void Solve()
{
    int last = -1;
	while (t--)
	{
		int v, x, k;
		Read(v), Read(x), Read(k);
		//if (last != -1)
		//	v ^= last, x ^= last, k ^= last;
		int now = Query(v, x); //从v点向上跳距离不超过x时,能跳到哪个点去 
	    if(out[now] - ins[now] < k) //如果以x为根的树结点总个数小于k则无解 
		   last = -1 ;
		else
		    last = hgt[Getans(T[ins[now]], T[out[now]], 1, n, out[now] - ins[now] - k + 1)];
		printf("%d
", last);
    }
}
int main()
{
	Read(n), Read(m), Read(t);
	for (int i = 1; i <= n; i++)
		Read(hgt[i]);
	for (int i = 1; i <= m; i++) 
		Read(S[i].x), Read(S[i].y), Read(S[i].v);
	Build();//重构树 
	Dfs(Getbel(1));
    sort(hgt + 1, hgt + n + 1);
	int k = unique(hgt + 1, hgt + n + 1) - hgt - 1;
	//离散化 
	for (int i = 1; i <= n; i++)
	{
		T[i] = T[i - 1];
		Insert(T[i], 1, k, lower_bound(hgt + 1, hgt + k + 1, h[i]) - hgt);
		//将各点高度加入到主席树中 
	}
	n = k;
	Solve();
}

  

原文地址:https://www.cnblogs.com/cutemush/p/11791397.html