Luogu 4197 Peaks
BZOJ 3545 带权限。
考虑离线,把所有边按照从小到大的顺序排序,把所有询问也按照从小到大的顺序排序,然后维护一个并查集和一个权值线段树,每处理一个询问就把比这个询问的$x$更小的边连上,具体来说就是合并两个并查集以及两棵线段树,查询的时候在线段树上走一走就好了。
要注意查询的第$k$大不是第$k$小,所以顺便再维护一个$siz$,如果$siz < k$那答案即为$-1$。
时间复杂度$O((m + q)logn)$。
Code:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 1e5 + 5; const int M = 5e5 + 5; const int inf = 1 << 30; int n, m, qn, a[N], mn = 0, mp[N], ufs[N], siz[N]; struct Innum { int val, id; friend bool operator < (const Innum &x, const Innum &y) { if(x.val == y.val) return x.id < y.id; else return x.val < y.val; } } in[N]; struct Pathway { int u, v, val; friend bool operator < (const Pathway &x, const Pathway &y) { return x.val < y.val; } } path[M]; struct Querys { int pos, val, k, id, res; friend bool operator < (const Querys &x, const Querys &y) { return x.val < y.val; } } q[M]; inline void read(int &X) { X = 0; char ch = 0; int op = 1; for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') op = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } inline void chkMax(int &x, int y) { if(y > x) x = y; } inline void discrete() { sort(in + 1, in + 1 + n); in[0].val = -inf; for(int cnt = 0, i = 1; i <= n; i++) { if(in[i].val != in[i - 1].val) ++cnt; chkMax(mn, cnt); a[in[i].id] = cnt; mp[cnt] = in[i].val; } } namespace PSegT { struct Node { int lc, rc, sum; } s[N * 60]; int root[N], nodeCnt = 0, top = 0, pool[N * 60]; #define lc(p) s[p].lc #define rc(p) s[p].rc #define sum(p) s[p].sum #define mid ((l + r) >> 1) inline void push(int x) { pool[++top] = x; } inline int newNode() { if(top) return pool[top--]; else return ++nodeCnt; } void ins(int &p, int l, int r, int x) { if(!p) p = newNode(); ++sum(p); if(l == r) return; if(x <= mid) ins(lc(p), l, mid, x); else ins(rc(p), mid + 1, r, x); } int go(int p, int l, int r, int x) { if(l == r) return sum(p); if(x <= mid) return go(lc(p), l, mid, x); else return go(rc(p), mid + 1, r, x); } int query(int p, int l, int r, int k) { if(l == r) return mp[l]; int now = sum(lc(p)); if(k <= now) return query(lc(p), l, mid, k); else return query(rc(p), mid + 1, r, k - now); } int merge(int u, int v, int l, int r) { if(!u || !v) return u + v; int p = newNode(); if(l == r) sum(p) = sum(u) + sum(v); else { lc(p) = merge(lc(u), lc(v), l, mid); rc(p) = merge(rc(u), rc(v), mid + 1, r); sum(p) = sum(lc(p)) + sum(rc(p)); } push(u), push(v); return p; } } using namespace PSegT; inline void init() { for(int i = 1; i <= n; i++) { ufs[i] = i; siz[i] = 1; ins(root[i], 1, mn, a[i]); } } int find(int x) { return ufs[x] == x ? x : ufs[x] = find(ufs[x]); } inline void merge(int x, int y) { int fx = find(x), fy = find(y); if(fx == fy) return; root[fx] = merge(root[fx], root[fy], 1, mn); ufs[fy] = fx; siz[fx] += siz[fy]; /* for(int i = 1; i <= mn; i++) printf("%d ", go(root[fx], 1, mn, i)); printf("\n"); */ } int main() { read(n), read(m), read(qn); for(int i = 1; i <= n; i++) { read(a[i]); in[i].val = a[i], in[i].id = i; } for(int i = 1; i <= m; i++) read(path[i].u), read(path[i].v), read(path[i].val); for(int i = 1; i <= qn; i++) read(q[i].pos), read(q[i].val), read(q[i].k), q[i].id = i; sort(path + 1, path + 1 + m), sort(q + 1, q + 1 + qn); /* printf("\n"); for(int i = 1; i <= m; i++) printf("%d %d %d\n", path[i].u, path[i].v, path[i].val); printf("\n"); for(int i = 1; i <= qn; i++) printf("%d %d %d\n", q[i].pos, q[i].val, q[i].k); printf("\n"); */ discrete(); init(); /* for(int i = 1; i <= n; i++) printf("%d ", a[i]); printf("\n"); */ for(int j = 1, i = 1; i <= qn; i++) { for(; j <= m && path[j].val <= q[i].val; ++j) merge(path[j].u, path[j].v); int now = find(q[i].pos); /* for(int k = 1; k <= mn; k++) printf("%d ", go(root[now], 1, mn, k)); printf("\n"); */ if(q[i].k > siz[now]) q[q[i].id].res = -1; else q[q[i].id].res = query(root[now], 1, mn, siz[now] - q[i].k + 1); } for(int i = 1; i <= qn; i++) printf("%d\n", q[i].res); return 0; }