BZOJ4009 权限题
真的不想再写一遍了
假设有果实$(x, y)$,询问$(a, b)$,用$st_i$表示$i$的$dfs$序,用$ed_i$表示所有$i$的子树搜完的$dfs$序,那么果实对询问产生贡献只会有两种情况:
1、这个果实表示的区间是一条链
不妨假设$dep_x < dep_y$,记$z$为$x$到$y$的树链上的从$x$向下走的第一个点,画个图可以发现$(a, b)$需要满足:
$st_a in [1, st_z - 1] cup [ed_z + 1, n], st_b in [st_y, ed_y]$,其中$(a, b)$可以互换。
2、这个果实表示的链是一条先向上再向下的纯正的树链
仍然画个图发现$(a, b)$需要满足:$st_a in [st_x, ed_x], st_b in [st_y, ed_y]$, $(a, b)$仍然可以互换。
对于每一个询问$(a, b)$,只要看一看有多少果实满足上面两种选一种的条件,然后求个$k$小就好了。
发现这其实是一个在二维矩阵中动态加点求$k$小的问题,这时候$KDTree$就出现了,然而我不会……
考虑扫描线降维,一个矩阵根据$x$坐标拆成两条线然后排个序扫一扫,每一次根据询问的点的位置进行加入删除的调整然后求个$k$小就好了。
也就是说只要写一个支持在一条线上区间加区间减然后求$k$小的数据结构就好了,我们需要一个外层权值内层下标的线段树兹磁这个操作。
瓶颈在于树套树的$O(nlog^2n)$。
最好标记永久化一下。
Code:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 4e4 + 5; const int M = 3e7 + 5; const int Lg = 18; int n, m, qn, lcnt = 0, mx = 0, num[N], tot = 0, head[N]; int dfsc = 0, st[N], ed[N], fa[N][Lg], dep[N], ans[N]; struct Edge { int to, nxt; } e[N << 1]; inline void add(int from, int to) { e[++tot].to = to; e[tot].nxt = head[from]; head[from] = tot; } struct Line { int pos, x, y, v, type; inline Line (int nowPos = 0, int nowX = 0, int nowY = 0, int nowV = 0, int nowType = 0) { pos = nowPos, x = nowX, y = nowY, v = nowV, type = nowType; } friend bool operator < (const Line &u, const Line &v) { return u.pos < v.pos; } } a[N << 3]; inline void addLine(int l1, int r1, int l2, int r2, int v) { a[++lcnt] = Line(l1, l2, r2, v, 1); a[++lcnt] = Line(r1 + 1, l2, r2, v, -1); } struct Querys { int x, y, k, id; friend bool operator < (const Querys &u, const Querys &v) { return u.x < v.x; } } q[N]; 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 swap(int &x, int &y) { int t = x; x = y; y = t; } void dfs(int x, int fat, int depth) { fa[x][0] = fat, dep[x] = depth, st[x] = ++dfsc; for(int i = 1; i <= 16; i++) fa[x][i] = fa[fa[x][i - 1]][i - 1]; for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if(y == fat) continue; dfs(y, x, depth + 1); } ed[x] = dfsc; } inline int getPos(int x, int stp) { int res = x; for(int i = 16; i >= 0; i--) if((stp >> i) & 1) res = fa[res][i]; return res; } namespace InSegT { struct Node { int lc, rc, sum; } s[M]; int nodeCnt = 0; #define lc s[p].lc #define rc s[p].rc #define sum(p) s[p].sum #define mid ((l + r) >> 1) void modify(int &p, int l, int r, int x, int y, int v) { if(!p) p = ++nodeCnt; if(x <= l && y >= r) { sum(p) += v; return; } if(x <= mid) modify(lc, l, mid, x, y, v); if(y > mid) modify(rc, mid + 1, r, x, y, v); } int query(int p, int l, int r, int x) { if(l == r) return sum(p); int res = sum(p); if(x <= mid) res += query(lc, l, mid, x); else res += query(rc, mid + 1, r, x); return res; } #undef lc #undef rc #undef sum } namespace OutSegT { using namespace InSegT; #define lc p << 1 #define rc p << 1 | 1 int root[N << 2]; void ins(int p, int l, int r, int x, int y, int pos, int type) { modify(root[p], 1, n, x, y, type); if(l == r) return; if(pos <= mid) ins(lc, l, mid, x, y, pos, type); else ins(rc, mid + 1, r, x, y, pos, type); } int getKth(int p, int l, int r, int x, int k) { if(l == r) return l; int now = query(root[lc], 1, n, x); if(k <= now) return getKth(lc, l, mid, x, k); else return getKth(rc, mid + 1, r, x, k - now); } } using namespace OutSegT; int main() { read(n), read(m), read(qn); for(int x, y, i = 1; i < n; i++) { read(x), read(y); add(x, y), add(y, x); } dfs(1, 0, 1); for(int x, y, v, i = 1; i <= m; i++) { read(x), read(y), read(v); num[++mx] = v; if(dep[x] > dep[y]) swap(x, y); if(st[x] <= st[y] && ed[x] >= ed[y]) { int z = getPos(y, dep[y] - dep[x] - 1); if(ed[z] < n) { addLine(st[y], ed[y], ed[z], n, v); addLine(ed[z], n, st[y], ed[y], v); } if(st[z] > 1) { addLine(st[y], ed[y], 1, st[z] - 1, v); addLine(1, st[z] - 1, st[y], ed[y], v); } } else { addLine(st[x], ed[x], st[y], ed[y], v); addLine(st[y], ed[y], st[x], ed[x], v); } } sort(num + 1, num + mx + 1); mx = unique(num + 1, num + 1 + mx) - num - 1; for(int i = 1; i <= lcnt; i++) a[i].v = lower_bound(num + 1, num + mx + 1, a[i].v) - num; for(int i = 1; i <= qn; i++) { read(q[i].x), read(q[i].y), read(q[i].k); q[i].x = st[q[i].x], q[i].y = st[q[i].y]; q[i].id = i; } sort(q + 1, q + 1 + qn); sort(a + 1, a + 1 + lcnt); for(int j = 1, i = 1; i <= qn; i++) { for(; j <= lcnt && a[j].pos <= q[i].x; ++j) ins(1, 1, mx, a[j].x, a[j].y, a[j].v, a[j].type); ans[q[i].id] = getKth(1, 1, mx, q[i].y, q[i].k); } for(int i = 1; i <= qn; i++) printf("%d ", num[ans[i]]); return 0; }