Description
在Bytemountains有座山峰,每座山峰有他的高度
。有些山峰之间有双向道路相连,共
条路径,每条路径有一个困难值,这个值越大表示越难走,现在有
组询问,每组询问询问从点
开始只经过困难值小于等于
的路径所能到达的山峰中第
高的山峰,如果无解输出
。
Input
第一行三个数,
,
。
第二行个数,第
个数为
接下来行,每行
个数
a b c
,表示从a到b有一条困难值为的双向路径。
接下来行,每行三个数
v x k
,表示一组询问。
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
HINT
。
Solution
日常套路,对询问按照从小到大排序,对边按照
从小到大排序,扫过去,然后线段树合并一波就行了。
#include<cstdio> #include<algorithm> #include<iostream> #include<cstring> #include<map> #include<cmath> #include<queue> #include<bitset> #define mk make_pair #define fi first #define se second #define pii pair<int,int> #define pb push_back #define sqr(x) ((x)*(x)) using namespace std; typedef long long ll; inline ll read() {ll x = 0; char ch = getchar(), w = 1;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;} void write(ll x) {if(x < 0) putchar('-'), x = -x;if(x > 9) write(x / 10);putchar(x % 10 + '0');} inline void writeln(ll x) {write(x);puts("");} const int N = 1e5 + 666, M = 6e5 + 6; int n, m, Q; int h[N]; struct Edge { int u, v, w; }e[M]; bool cmp(Edge a, Edge b) { return a.w < b.w; } struct Que { int v, x, k, i; }q[M]; bool cmpq(Que a, Que b) { return a.x < b.x; } int ans[M], fa[N]; int getf(int x) { return fa[x] == x ? x : fa[x] = getf(fa[x]); } int rt[N]; struct T { int l, r, c; }t[N * 32]; int merge(int x, int y) { if(!(x && y)) return x|y; t[x].c += t[y].c; t[x].l = merge(t[x].l, t[y].l); t[x].r = merge(t[x].r, t[y].r); return x; } void HB(int x, int y) { int u = getf(x), v = getf(y); if(u == v) return; fa[u] = v; rt[v] = merge(rt[u], rt[v]); } int tot; void build(int &o, int l, int r, int x) { o = ++tot; t[o].c = 1; if(l == r) return; int mid = (l + r) >> 1; if(x <= mid) build(t[o].l, l, mid, x); else build(t[o].r, mid + 1, r, x); } int query(int o, int x, int l, int r) { if(t[o].c < x) return -1; if(l == r) return l; int val = t[o].c - t[t[o].l].c; int mid = (l + r) >>1; if(x <= val) return query(t[o].r, x, mid + 1, r); else query(t[o].l, x - val, l, mid); } int main() { n = read(), m = read(), Q = read(); for(int i = 1; i <= n; ++i) h[i] = read(), build(rt[i], 1, 1e9, h[i]); for(int i = 1; i <= m; ++i) { e[i].u = read(), e[i].v = read(), e[i].w = read(); } sort(e + 1, e + 1 + m, cmp); for(int i = 1; i <= Q; ++i) { q[i].v = read(), q[i].x = read(), q[i].k = read(); q[i].i = i; } sort(q + 1, q +1 + Q, cmpq); int p = 1; for(int i = 1; i <= n; ++i) fa[i] = i; for(int i = 1; i <= Q; ++i) { while(p <= m && e[p].w <= q[i].x) HB(e[p].u, e[p].v), ++p; ans[q[i].i] = query(rt[getf(q[i].v)], q[i].k, 1, 1e9); } for(int i = 1; i <= Q; ++i) writeln(ans[i]); return 0; }
BZOJ3551
建立kruskal重构树。其满足以下性质:
- 二叉树(好吧这题意义不大)
- 原树与新树两点间路径上边权(点权)的最大值相等
- 子节点的边权小于等于父亲节点(大根堆)
- 原树中两点之间路径上边权的最大值等于新树上两点的LCA的点权
倍增找到最后一个的
,在其权值线段树下找第
大的数。注意
的时候,输出
的最大值。
#include<cstdio> #include<algorithm> #include<iostream> #include<cstring> #include<map> #include<cmath> #include<queue> #include<bitset> #define mk make_pair #define fi first #define se second #define pii pair<int,int> #define pb push_back #define sqr(x) ((x)*(x)) using namespace std; typedef long long ll; inline ll read() {ll x = 0; char ch = getchar(), w = 1;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;} void write(ll x) {if(x < 0) putchar('-'), x = -x;if(x > 9) write(x / 10);putchar(x % 10 + '0');} inline void writeln(ll x) {write(x);puts("");} const int N = 2e5 + 666; int n, m, Q; int a[N]; struct Edge { int u, v, w; }e[N * 3]; bool cmp(Edge a, Edge b) { return a.w < b.w; } int fa[N]; int getf(int x) { return fa[x] == x ? x : fa[x] = getf(fa[x]); } int tot; int v[N]; struct G { struct Edge { int u, v, nxt; }e[N * 2]; int head[N], en; void addedge(int x, int y) { e[++en].u = x, e[en].v = y, e[en].nxt = head[x], head[x] = en; e[++en].u = y, e[en].v = x, e[en].nxt = head[y], head[y] = en; } bool vis[N]; int f[N][19], rt[N], num; struct Tree { int l, r, c; }t[N*30]; void build(int &o, int l, int r, int x) { o = ++num; t[o].c = 1; if(l == r) return; int mid = (l + r) >> 1; if(x <= mid) build(t[o].l, l, mid, x); else build(t[o].r, mid + 1, r, x); } int merge(int x, int y) { if(!(x && y)) return x|y; int o = ++num; t[o].c = t[x].c + t[y].c; t[o].l = merge(t[x].l, t[y].l); t[o].r = merge(t[x].r, t[y].r); //尽量不要直接合并 return o; /* t[x].c = t[x].c + t[y].c; t[x].l = merge(t[x].l, t[y].l); t[x].r = merge(t[x].r, t[y].r); return x;*/ } void dfs(int x, int F) { vis[x] = 1; f[x][0] = F; for(int i =1; i <= 18; ++i) f[x][i] = f[f[x][i - 1]][i - 1]; if(x <= n) { build(rt[x], 1, 1e9, a[x]); } else rt[x] = ++num; for(int i = head[x]; i;i = e[i].nxt) { int y = e[i].v; if(y == F) continue; dfs(y, x); rt[x] = merge(rt[x], rt[y]); //不能直接赋值,像这样合并可能就直接rt[x]=rt[y]直接出错 } } void init() { for(int i = tot; i; --i) if(!vis[i]) dfs(i, i); } int get(int x, int val) { for(int i = 18;~i;--i) if(val >= v[f[x][i]]) x = f[x][i]; return x; } int query(int o, int l, int r, int x) { if(t[o].c < x) return -1; if(l == r) return l; int mid = (l + r) >> 1; int val = t[t[o].r].c ; if(val >= x) return query(t[o].r, mid + 1, r, x); else return query(t[o].l, l, mid, x - val); } }g; int main() { int mx = 0; n = read(), m = read(), Q = read(); for(int i = 1; i <= n; ++i) a[i] = read(), fa[i] = i, mx = max(mx, a[i]); for(int i = 1; i <= m; ++i) { e[i].u = read(), e[i].v = read(), e[i].w = read(); } sort(e + 1, e + 1 + m, cmp); tot = n; for(int i = 1; i <= m; ++i) { int x = getf(e[i].u), y = getf(e[i].v); if(x == y) continue; ++tot; g.addedge(x, tot); g.addedge(y, tot); v[tot] = e[i].w; fa[x] = tot; fa[y] = tot; fa[tot] = tot; } g.init(); int ans = 0; while(Q--) { int v = read(), x = read(), k = read(); v ^= ans; x ^= ans; k ^= ans; int y = g.get(v, x); if(k == 0) writeln(ans = mx); else writeln(ans = g.query(g.rt[y], 1, 1e9, k)); if(ans == -1) ans = 0; } return 0; }