传送门
给出一棵nnn个点的树。接下来给出PPP条树上路径ai→bia_i\to b_iai→bi,及其权值cic_ici。最后有QQQ个询问,每个询问给出一条树上路径ui→viu_i\to v_iui→vi,问在包含ui→viu_i\to v_iui→vi的所有树上路径中(包含指ui→viu_i\to v_iui→vi是ai→bia_i\to b_iai→bi的子路径),权值第kkk小的路径权值是多少?
不妨设dep[u]<dep[v]dep[u]<dep[v]dep[u]<dep[v],
若lca(u,v)=ulca(u,v)=ulca(u,v)=u,记ppp是uuu在向vvv方向的儿子,
那么包含u→vu\to vu→v的路径a→ba\to ba→b一定满足:a∉subtreep,b∈subtreeva\not\in subtree_p,b\in subtree_va∈subtreep,b∈subtreev
若lca(u,v)≠ulca(u,v)\not=ulca(u,v)=u,
那么包含u→vu\to vu→v的路径a→ba\to ba→b一定满足:a∈subtreeu,b∈subtreeva\in subtree_u,b\in subtree_va∈subtreeu,b∈subtreev
也就是说,对于包含u→vu\to vu→v的路径a→ba\to ba→b,dfn[a],dfn[b]dfn[a],dfn[b]dfn[a],dfn[b]的取值范围是 一段或两端区间。
因此所有符合条件的路径a→ba\to ba→b可以用二维平面上的 一个或两个矩形 表示出来。
原题要求的就是所有包含询问点的矩形中权值第kkk小的。
考虑整体二分,统计一个点在多少个 权值在[l,r][l,r][l,r]内的矩形 中出现过。用扫描线解决。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=80010;
struct Edge{int v,nxt;}edge[N];
int n,m,q,cnt,head[N];
int fa[N][20],dep[N],ind,dfn[N],lst[N];
int tot,ans[N],sum[N];
struct Rectangle{int xd,xu,yd,yu,v;}rect[N];
bool operator < (Rectangle a,Rectangle b){return a.v<b.v;}
struct Point{int x,y,k,id;}pt[N],tmp1[N],tmp2[N];
struct Line{int x,yd,yu,v,id;}line[N];
bool operator < (Line a,Line b){return a.x==b.x?a.id<b.id:a.x<b.x;}
struct Bit{int val[N];void modify(int l,int r,int v){for(int i=l;i<=n;i+=(i&(-i))) val[i]+=v;for(int i=r+1;i<=n;i+=(i&(-i))) val[i]-=v;}int query(int x){int res=0;for(;x;x-=(x&(-x))) res+=val[x];return res;}
}T;
void add(int u,int v){edge[++cnt].v=v;edge[cnt].nxt=head[u];head[u]=cnt;
}
void dfs(int u){dfn[u]=++ind;for(int i=0;fa[u][i];i++) fa[u][i+1]=fa[fa[u][i]][i];for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].v;if(v==fa[u][0]) continue;fa[v][0]=u;dep[v]=dep[u]+1;dfs(v);}lst[u]=ind;
}
int jump(int u,int dis){for(int i=18;dis;i--){if(dis>=(1<<i)){dis-=(1<<i);u=fa[u][i];}}return u;
}
int LCA(int u,int v){if(dep[u]<dep[v]) swap(u,v);u=jump(u,dep[u]-dep[v]);if(u==v) return u;for(int i=18;i>=0;i--) if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];return fa[u][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[pt[i].id]=rect[l].v;return;}int mid=(l+r)>>1,siz=0;for(int i=l;i<=mid;i++){line[++siz]=(Line){rect[i].xd,rect[i].yd,rect[i].yu,1,0};line[++siz]=(Line){rect[i].xu,rect[i].yd,rect[i].yu,-1,n+1};}for(int i=st;i<=ed;i++) line[++siz]=(Line){pt[i].x,pt[i].y,0,0,i};sort(line+1,line+siz+1);for(int i=1;i<=siz;i++){if(st<=line[i].id&&line[i].id<=ed) sum[line[i].id]=T.query(line[i].yd);else T.modify(line[i].yd,line[i].yu,line[i].v);}int a=0,b=0;for(int i=st;i<=ed;i++){if(sum[i]>=pt[i].k) tmp1[++a]=pt[i];else tmp2[++b]=(Point){pt[i].x,pt[i].y,pt[i].k-sum[i],pt[i].id};}for(int i=st;i<=st+a-1;i++) pt[i]=tmp1[i-st+1];for(int i=st+a;i<=ed;i++) pt[i]=tmp2[i-st-a+1];solve(l,mid,st,st+a-1);solve(mid+1,r,st+a,ed);
}
int main(){scanf("%d%d%d",&n,&m,&q);for(int i=1;i<n;i++){int a,b;scanf("%d%d",&a,&b);add(a,b);add(b,a);}dfs(1);for(int i=1;i<=m;i++){int a,b,c;scanf("%d%d%d",&a,&b,&c);int u=LCA(a,b);if(dfn[a]>dfn[b]) swap(a,b);if(u!=a) rect[++tot]=(Rectangle){dfn[a],lst[a],dfn[b],lst[b],c};else{int w=jump(b,dep[b]-dep[a]-1);rect[++tot]=(Rectangle){1,dfn[w]-1,dfn[b],lst[b],c};if(lst[w]<n) rect[++tot]=(Rectangle){dfn[b],lst[b],lst[w]+1,n,c};}}sort(rect+1,rect+tot+1);for(int i=1;i<=q;i++){int a,b,k;scanf("%d%d%d",&a,&b,&k);if(dfn[a]>dfn[b]) swap(a,b);pt[i]=(Point){dfn[a],dfn[b],k,i};}solve(1,tot,1,q);for(int i=1;i<=q;i++) printf("%d\n",ans[i]);return 0;
}