也是我做的第一个整体二分题吧。
主要是要分析每一个盘子的贡献。包含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
#include
#include
#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< dep[y]) swap(x, y);
for(int i = 15; i >= 0; --i) if(dep[y] - dep[x] >= (1<= 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;
}