也是我做的第一个整体二分题吧。
主要是要分析每一个盘子的贡献。包含path(u, v)这条路径的路径,记为(x, y)。(u比v的dfn小,x比y的小)那么有两种情况。
①u不是v的祖先
此时,x与y势必要分别为u的子树、v的子树(包含u、v)。
②u是v的祖先
这个时候就较为麻烦。首先,一个端点肯定在v的子树中(包含v)。
而另一个端点,较为麻烦。令w为u的儿子,且同时为v的祖先。那么这个端点可以取w这棵子树(包含w)外的任何一点。
考虑清楚贡献,我们就可以离线下来整体二分了。
给所有盘子按权值升序排,然后分治。每次看前一半对每一个水果的贡献(即,包含多少盘子)。就像主席树那样,如果包含的盘子数目≥k,就放入[l, mid]中分治,否则放入[mid + 1, r]。(如果丢进右区间,不要忘记对k操作)
#include <cstdio> #include <cstring> #include <algorithm> #define N 40010 using namespace std; inline char gc() { static char now[1<<16], *S, *T; if(S == T) {T = (S = now) + fread(now, 1, 1<<16, stdin); if(S == T) return EOF;} return *S++; } inline int read() { int x = 0, f = 1; char c = gc(); while(c < '0' || c > '9') {if(c == '-') f = -1; c = gc();} while(c >= '0' && c <= '9') {x = x * 10 + c - 48; c = gc();} return x * f; } struct edge {int to, next;}e[N<<1]; struct Plate {int x1, y1, x2, y2, v;}plate[N<<1]; inline bool operator < (const Plate &A, const Plate &B) {return A.v < B.v;} struct Fruit {int x, y, k, id;}fruit[N], temp1[N], temp2[N]; struct scanLine {int x, y1, y2, v, pos;}line[N<<1]; inline bool operator < (const scanLine &A, const scanLine &B) {return (A.x == B.x)?(A.pos < B.pos):(A.x < B.x);} int head[N], fa[N][16], dep[N], dfn[N], last[N], ans[N], sum[N], Bit[N]; int n, P, Q, cnt = 1, tim = 0, plt = 0, sz; inline void modify(int l, int r, int v) { for(int i = l; i <= n; i+= i & -i) Bit[i]+= v; for(int i = r + 1; i <= n; i+= i & -i) Bit[i]-= v; } inline int query(int p) {int ret = 0; for(; p; p-= p & -p) ret+= Bit[p]; return ret;} inline void ins(int x, int y) {e[++cnt].to = y; e[cnt].next = head[x]; head[x] = cnt;} void dfs(int x, int f, int d) { fa[x][0] = f; dep[x] = d; dfn[x] = ++tim; for(int i = head[x]; i; i = e[i].next) if(e[i].to != f) dfs(e[i].to, x, d + 1); last[x] = tim; } inline void init() { for(int j = 1; j < 16; ++j) for(int i = 1; i <= n; ++i) fa[i][j] = fa[fa[i][j - 1]][j - 1]; } inline int jump1(int x, int d) {for(int i = 15; d; --i) if(d >= (1<<i)) d-= (1<<i), x = fa[x][i]; return x;} inline int getlca(int x, int y) { if(dep[x] > dep[y]) swap(x, y); for(int i = 15; i >= 0; --i) if(dep[y] - dep[x] >= (1<<i)) y = fa[y][i]; if(x == y) return x; for(int i = 15; i >= 0; --i) if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i]; return fa[x][0]; } void solve(int l, int r, int st, int ed) { if(st > ed) return ; if(l == r) {for(int i = st; i <= ed; ++i) ans[fruit[i].id] = plate[l].v; return ;} int mid = (l + r)>>1; sz = 0; for(int i = l; i <= mid; ++i) { line[++sz] = (scanLine){plate[i].x1, plate[i].x2, plate[i].y2, 1, 0}; line[++sz] = (scanLine){plate[i].y1, plate[i].x2, plate[i].y2, -1, n + 1}; } for(int i = st; i <= ed; ++i) line[++sz] = (scanLine){fruit[i].x, fruit[i].y, 0, 0, i}; sort(line+1, line+sz+1); for(int i = 1; i <= sz; ++i) { if(st <= line[i].pos && line[i].pos <= ed) sum[line[i].pos] = query(line[i].y1); else modify(line[i].y1, line[i].y2, line[i].v); } int tpa, tpb; tpa = tpb = 0; for(int i = st; i <= ed; ++i) { if(sum[i] >= fruit[i].k) temp1[++tpa] = fruit[i]; else temp2[++tpb] = (Fruit){fruit[i].x, fruit[i].y, fruit[i].k - sum[i], fruit[i].id}; } for(int i = st; i <= st + tpa - 1; ++i) fruit[i] = temp1[i - st + 1]; for(int i = st + tpa; i <= ed; ++i) fruit[i] = temp2[i - st - tpa + 1]; solve(l, mid, st, st + tpa - 1); solve(mid + 1, r, st + tpa, ed); } int main() { n = read(); P = read(); Q = read(); for(int i = 1; i < n; ++i) {int x = read(), y = read(); ins(x, y); ins(y, x);} dfs(1, 0, 1); init(); for(int i = 1; i <= P; ++i) { int a = read(), b = read(), c = read(); if(dfn[a] > dfn[b]) swap(a, b); int w = getlca(a, b); if(w != a) plate[++plt] = (Plate){dfn[a], last[a], dfn[b], last[b], c}; else { int u = jump1(b, dep[b] - dep[a] - 1); plate[++plt] = (Plate){1, dfn[u] - 1, dfn[b], last[b], c}; if(last[u] < n) plate[++plt] = (Plate){dfn[b], last[b], last[u] + 1, n, c}; } } sort(plate+1, plate+plt+1); for(int i = 1; i <= Q; ++i) { int u = read(), v = read(), k = read(); if(dfn[u] > dfn[v]) swap(u, v); fruit[i] = (Fruit){dfn[u], dfn[v], k, i}; }memset(Bit, 0, sizeof(Bit)); solve(1, plt, 1, Q); for(int i = 1; i <= Q; ++i) printf("%d\n", ans[i]); return 0; }