给出一棵 n n n个点的树。接下来给出 P P P条树上路径 a i → b i a_i\to b_i ai→bi,及其权值 c i c_i ci。最后有 Q Q Q个询问,每个询问给出一条树上路径 u i → v i u_i\to v_i ui→vi,问在包含 u i → v i u_i\to v_i ui→vi的所有树上路径中(包含指 u i → v i u_i\to v_i ui→vi是 a i → b i a_i\to b_i ai→bi的子路径),权值第 k k k小的路径权值是多少?
不妨设
d
e
p
[
u
]
<
d
e
p
[
v
]
dep[u]<dep[v]
dep[u]<dep[v],
若
l
c
a
(
u
,
v
)
=
u
lca(u,v)=u
lca(u,v)=u,记
p
p
p是
u
u
u在向
v
v
v方向的儿子,
那么包含
u
→
v
u\to v
u→v的路径
a
→
b
a\to b
a→b一定满足:
a
∉
s
u
b
t
r
e
e
p
,
b
∈
s
u
b
t
r
e
e
v
a\not\in subtree_p,b\in subtree_v
a∈subtreep,b∈subtreev
若
l
c
a
(
u
,
v
)
≠
u
lca(u,v)\not=u
lca(u,v)=u,
那么包含
u
→
v
u\to v
u→v的路径
a
→
b
a\to b
a→b一定满足:
a
∈
s
u
b
t
r
e
e
u
,
b
∈
s
u
b
t
r
e
e
v
a\in subtree_u,b\in subtree_v
a∈subtreeu,b∈subtreev
也就是说,对于包含
u
→
v
u\to v
u→v的路径
a
→
b
a\to b
a→b,
d
f
n
[
a
]
,
d
f
n
[
b
]
dfn[a],dfn[b]
dfn[a],dfn[b]的取值范围是 一段或两端区间。
因此所有符合条件的路径
a
→
b
a\to b
a→b可以用二维平面上的 一个或两个矩形 表示出来。
原题要求的就是所有包含询问点的矩形中权值第 k k k小的。
考虑整体二分,统计一个点在多少个 权值在 [ 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;
}