分析

读了下题,立马就想到了主席树,对每个点存一个值域线段树,存储其到根的路径上有哪些点,\(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;
}




posted on 2021-11-12 16:17  漠寒·  阅读(51)  评论(0编辑  收藏  举报