分析
读了下题,立马就想到了主席树,对每个点存一个值域线段树,存储其到根的路径上有哪些点,\(x\) 到 \(y\) 的路径就是他们线段树合起来再减去两倍 \(lca\) 的线段树,再把 \(lca\) 本身所含点加上即可。
然后发现每个点就比他爹线段树多含了它本身所含的点(就是那些住它那儿的)。所以主席树就可以轻松处理这个问题了。
需要注意的是,一个城市可能含多个住民,所以在修改线段树的儿子时需要注意是继承上次修改的结果,还是继承它父亲的对应位置。
代码
#include<bits/stdc++.h>
using namespace std;
inline void read(int &res){
char c;
int f=1;
res=0;
c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')res=(res<<1)+(res<<3)+c-48,c=getchar();
res*=f;
}
int n,m,q;
vector<int>v[100005];
int head[100005],tot;
struct edge{
int to,nex;
}e[200005];
inline void add(int qq,int mm){
e[++tot].to=mm;
e[tot].nex=head[qq];
head[qq]=tot;
}
int root[100005],cnt;
struct node{
int sum,ls,rs;
}a[2000000];
void build(int &rt,int l,int r){
if(!rt)rt=++cnt;
if(l==r)return;
int mid=(l+r)>>1;
build(a[rt].ls,l,mid);
build(a[rt].rs,mid+1,r);
}
void modify(int rt,int rtt,int l,int r,int x){
if(l==r){
a[rtt].sum++;
return;
}
int mid=(l+r)>>1;
if(mid>=x){
if(a[rtt].ls){//每次修改的时候都要新建一次,要不你会把你爹的东西也改了(改了一个小时)
int k=++cnt;
a[k]=a[a[rtt].ls];
a[rtt].ls=k;
}
else {
int k=++cnt;
a[k]=a[a[rt].ls];
a[rtt].ls=k;
}
if(!a[rtt].rs)a[rtt].rs=a[rt].rs;
modify(a[rt].ls,a[rtt].ls,l,mid,x);
}
else {
if(a[rtt].rs){
int k=++cnt;
a[k]=a[a[rtt].rs];
a[rtt].rs=k;
}
else {
int k=++cnt;
a[k]=a[a[rt].rs];
a[rtt].rs=k;
}
if(!a[rtt].ls){
a[rtt].ls=a[rt].ls;
}
modify(a[rt].rs,a[rtt].rs,mid+1,r,x);
}
a[rtt].sum=a[a[rtt].ls].sum+a[a[rtt].rs].sum;
}
int fa[100005][17],dep[100005];
void dfs(int u,int f){
fa[u][0]=f;
dep[u]=dep[f]+1;
root[u]=++cnt;
a[root[u]]=a[root[f]];//可能没有居民住这儿,所以先处理一下
for(int i=0;i<v[u].size();i++){
modify(root[f],root[u],1,m,v[u][i]);
}
for(int i=head[u];i;i=e[i].nex){
int vv=e[i].to;
if(vv==f)continue;
dfs(vv,u);
}
}
void init(){//lca倍增写法
for(int i=1;i<=16;i++){
for(int j=1;j<=n;j++){
fa[j][i]=fa[fa[j][i-1]][i-1];
}
}
}
int lca(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=16;i>=0;i--){
if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
}
if(x==y)return x;
for(int i=16;i>=0;i--){
if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
}
return fa[x][0];
}
int query(int rt,int rtt,int rrt,int tr,int l,int r,int k){
if(l==r)return l;
int mid=(l+r)>>1,sa=a[a[rt].ls].sum+a[a[rtt].ls].sum-a[a[rrt].ls].sum-a[a[tr].ls].sum;//这应该是主席树最基础的东西吧
if(sa>=k)return query(a[rt].ls,a[rtt].ls,a[rrt].ls,a[tr].ls,l,mid,k);
else return query(a[rt].rs,a[rtt].rs,a[rrt].rs,a[tr].rs,mid+1,r,k-sa);
}
signed main()
{
read(n);read(m);read(q);
for(int i=1;i<n;i++){
int x,y;
read(x);read(y);
add(x,y);
add(y,x);
}
for(int i=1;i<=m;i++){
int x;
read(x);
v[x].push_back(i);
}
build(root[0],1,n);//预处理,好像不写这个也可以
dfs(1,0);
init();
for(int i=1;i<=q;i++){
int x,y,aa;
read(x);read(y);read(aa);
int gg=lca(x,y);
int kk=fa[gg][0];
int ss=a[root[x]].sum+a[root[y]].sum-a[root[kk]].sum-a[root[gg]].sum;
if(aa>ss)aa=ss;
printf("%d ",aa);
for(int j=1;j<=aa;j++){
printf("%d ",query(root[x],root[y],root[kk],root[gg],1,m,j));
}
puts("");
}
return 0;
}