标签: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