【主席树】Count on a tree

【描述】
给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。
【输入】
第一行两个整数N,M。
第二行有N个整数,其中第i个整数表示点i的权值。
后面N-1行每行两个整数(x,y),表示点x到点y有一条边。
最后M行每行两个整数(u,v,k),表示一组询问
【输出】
M行,表示每个询问的答案。最后一个询问不输出换行符

【思路】

我们先来考虑序列第k大。我们查询[l,r]的情况,变成查询r和l-1。那么树上也可以类似操作。我们在每个节点维护其到根节点的信息。路径上的信息可以通过 s i z u + s i z v s i z l c a ( u , v ) s i z f a [ l c a ( u , v ) ] siz_u+siz_v-siz_{lca(u,v)}-siz_{fa[lca(u,v)]} 得到。所以我们只需要同时在四个点对应的树上查询即可。
代码:

#include<bits/stdc++.h>
#include<tr1/unordered_map>
#define re register
using namespace std;
const int N=1e5+5;
int n,m,a,b,k,c[N],val[N],tot,rt[N],las;
tr1::unordered_map<int,int>f;
inline int red()
{
    int data=0;int w=1; char ch=0;
    ch=getchar();
    while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
    if(ch=='-') w=-1,ch=getchar();
    while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
    return data*w;
}
namespace tree{
	int ch[N*30|1][2],siz[N*30|1],cnt=0;
	int change(int v,int l,int r,int pos)
	{
		int u=++cnt;siz[u]=siz[v]+1;
		if(l==r)return cnt;
		int mid=(l+r)>>1;
		ch[u][0]=ch[v][0];ch[u][1]=ch[v][1];
		if(pos<=mid)ch[u][0]=change(ch[v][0],l,mid,pos);
		else ch[u][1]=change(ch[v][1],mid+1,r,pos);
		return u;
	}
	int query(int a,int b,int c,int d,int l,int r,int k)
	{
		if(l==r)return l;
		int ls=siz[ch[a][0]]+siz[ch[b][0]]-siz[ch[c][0]]-siz[ch[d][0]];
		int mid=(l+r)>>1;
		if(ls>=k)return query(ch[a][0],ch[b][0],ch[c][0],ch[d][0],l,mid,k);
		else return query(ch[a][1],ch[b][1],ch[c][1],ch[d][1],mid+1,r,k-ls);
	}
}
using tree::change;
using tree::query;
namespace LCA{
	vector<int>g[N];
	int dep[N]={0,1},fa[N][18];
	void dfs(int u){
		rt[u]=change(rt[fa[u][0]],1,tot,f[val[u]]);
		for(int re i=1;(1<<i)<=(dep[u]);i++)fa[u][i]=fa[fa[u][i-1]][i-1];
		for(int re i=g[u].size()-1;~i;--i){int v=g[u][i];if(!dep[v])dep[v]=dep[u]+1,fa[v][0]=u,dfs(v);}
	}
	inline int lca(int a,int b)
	{
		if(dep[a]<dep[b])swap(a,b);
		int t=dep[a]-dep[b];
		for(int re i=0;(1<<i)<=t;++i)
			if(t&(1<<i))a=fa[a][i];
		if(a==b)return a;
		for(int re i=17;~i;--i)
			if(fa[a][i]!=fa[b][i])a=fa[a][i],b=fa[b][i];
		return fa[a][0];
	}
}
using LCA::g;
using LCA::dfs;
using LCA::lca;
int main()
{
	n=red();m=red();
	for(int re i=1;i<=n;i++)c[i]=val[i]=red();
	sort(c+1,c+(tot=n)+1);tot=unique(c+1,c+n+1)-c-1;
	for(int re i=1;i<=tot;i++)f[c[i]]=i;
	for(int re i=1;i^n;i++)a=red(),b=red(),g[a].push_back(b),g[b].push_back(a);dfs(1);
	while(m--){
		a=red()^las;b=red();k=red();int lc1=lca(a,b),lc2=LCA::fa[lc1][0];
		printf("%d",las=c[query(rt[a],rt[b],rt[lc1],rt[lc2],1,tot,k)]);if(m)putchar('\n');
	}
}
发布了106 篇原创文章 · 获赞 22 · 访问量 5493

猜你喜欢

转载自blog.csdn.net/weixin_44111457/article/details/102022394
今日推荐