洛谷P2633 Count on a tree 题解
题意:给定一棵树和 $u,v,k$ ,求 $u,v$ 结点间的第 $k$ 小点权
本来以为是个树链剖分+主席树,结果发现并不可做
观察到主席树本质上利用了前缀和来计算线性的区间和
现在就是把求和搬到了树上
可以立即想到树上差分
不过这道题不是树上差分,可能应该叫树上前缀和更恰当一些
对于一条根结点(无根树就假定是 $1$ 好了)到叶子结点的简单路径,就是一个线性区间上的主席树
也就是在树上建立了类似前缀和体系的主席树群,每一个主席树维护了一条根结点到叶子结点的路径上的结点的信息
感性理解一下,有点“前缀主席树”的感觉
那么 $u$ 到 $v$ 的信息就是
sum[u]+sum[v]-sum[lca(u,v)]-sum[fa[lca(u,v)][0]]
是不是很像树上差分了
其他的比如离散化什么的都是基本操作了,这里不再赘述了 qwq
时间复杂度 $O(n\log n)$
代码如下
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define gc() getchar()
#define pc(a) putchar(a)
template<typename T>inline void read(T &k)
{
char ch=gc();T x=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
k=x*f;
}
template<typename T>inline void write(T k)
{
if(k<0){k=-k;pc('-');}if(k>9)write(k/10);pc(k%10+'0');
}
#define MAXN (int)(1e5+5)
struct node
{
int u,v,next;
}e[MAXN<<1];
int n,m,Q,last,a[MAXN],b[MAXN],tot;
int head[MAXN],pos=1,fa[MAXN][22],lg[MAXN];
int L[MAXN<<5],R[MAXN<<5],sum[MAXN<<5],T[MAXN<<5],dep[MAXN];
void addEdge(int u,int v)
{
e[pos]={u,v,head[u]};
head[u]=pos++;
}
int build(int l,int r)
{
int at=++tot;
if(l<r)
{
int mid=(l+r)>>1;
L[at]=build(l,mid);
R[at]=build(mid+1,r);
}
return at;
}
int insert(int pre,int l,int r,int k)
{
int at=++tot;
L[at]=L[pre];R[at]=R[pre];sum[at]=sum[pre]+1;
if(l<r)
{
int mid=(l+r)>>1;
if(k<=mid)L[at]=insert(L[pre],l,mid,k);
else R[at]=insert(R[pre],mid+1,r,k);
}
return at;
}
void dfs(int u,int f)
{
fa[u][0]=f;dep[u]=dep[f]+1;
T[u]=insert(T[f],1,m,a[u]);
for(int i=1; i<=lg[dep[u]]; i++)
fa[u][i]=fa[fa[u][i-1]][i-1];
for(int i=head[u]; i; i=e[i].next)
{
int v=e[i].v;
if(v==f)continue;
dfs(v,u);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
while(dep[x]>dep[y])x=fa[x][lg[dep[x]-dep[y]]-1];
if(x==y)return x;
for(int i=lg[dep[x]]; i>=0; i--)
{
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
}
return fa[x][0];
} // 倍增求LCA
int query(int x,int y,int z,int t,int l,int r,int k)
{
if(l==r)return l;
int mid=(l+r)>>1;
int res=sum[L[x]]+sum[L[y]]-sum[L[t]]-sum[L[z]];
if(k<=res)return query(L[x],L[y],L[z],L[t],l,mid,k);
else return query(R[x],R[y],R[z],R[t],mid+1,r,k-res);
}
void init()
{
sort(b+1,b+1+n);
m=unique(b+1,b+1+n)-b-1;
for(int i=1; i<=n; i++)
a[i]=lower_bound(b+1,b+1+m,a[i])-b;
for(int i=1; i<=n; i++)
lg[i]=lg[i-1]+(1<<lg[i-1]==i);
build(1,m);
dfs(1,0);
}
signed main()
{
read(n);read(Q);
for(int i=1; i<=n; i++)
read(a[i]),b[i]=a[i];
for(int i=1,u,v; i<n; i++)
{
read(u);read(v);
addEdge(u,v);addEdge(v,u);
}init();
while(Q--)
{
int x,y,k;
read(x);read(y);read(k);x^=last; // 强制在线
int t=lca(x,y);
write(last=b[query(T[x],T[y],T[fa[t][0]],T[t],1,m,k)]);pc('\n');
}
return 0;
}