标签:std -- 重构 oid mount mit sam byte algorithm
[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\n", 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();
}
标签:std -- 重构 oid mount mit sam byte algorithm
原文地址:https://www.cnblogs.com/cutemush/p/11791397.html