P2633 Count on a tree

P2633 Count on a tree

静态第 \(k\) 小,想到主席树。

但是主席树大多数情况下是序列问题,这道题在树上。

想一想之前写过的第 \(k\) 小板子,发现主席树是具有前缀性质的。

于是在这道题中,考虑将原本主席树中:\(i\) 棵线段树维护前 \(i\) 个数的出现次数 改为 \(i\) 棵线段树表示结点 \(i\) 到根的路径中数的出现次数

然后根据这个维护前缀的定义,取出 \(u->v\) 路径上这一段数时,将路径分为 \(3\) 部分

  1. \(u->LCA\)下面一层,这就是 \(sum(u)-sum(LCA)\)
  2. \(v-> LCA\)下面一层,就是 \(sum(v)-sum(LCA)\)
  3. \(LCA\)\(sum(LCA)-sum(fa_{LCA})\)

将这些加起来就是这条路径上对应主席树的位置。

即将查询时将原本的

sum(ls(u))-sum(ls(v))

改为:

sum(ls(u))+sum(ls(v))-sum(ls(lca))-sum(ls(falca))

就可以了。

#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
    x=0;char ch=getchar();bool f=false;
    while(!isdigit(ch)) f|=ch=='-',ch=getchar();
    while(isdigit(ch))  x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    x=f?-x:x;return;
}
template <typename T>
inline void print(T x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) print(x/10);
    putchar(x%10^48);return;
}
const int N=1e5+3,M=1e5+3;
struct Seg_Tree{
    int ls,rs,sum;
    #define ls(x)   c[x].ls
    #define rs(x)   c[x].rs
    #define sum(x)  c[x].sum
}c[N*32];
int rt[N],cnt,head[N],to[N<<1],Next[N<<1],tot,n,m,len;
int a[N],b[N],siz[N],son[N],fa[N],top[N],dep[N];
//Graph
inline void Add_edge(int u,int v){Next[++tot]=head[u],head[u]=tot,to[tot]=v;return;}
//Seg_Tree
inline void Push_up(int x){sum(x)=sum(ls(x))+sum(rs(x));return;}

inline void Build(int &x,int l,int r){
    x=++cnt;
    if(l==r)    return;
    int mid=(l+r)>>1;
    Build(ls(x),l,mid),Build(rs(x),mid+1,r);
}

inline void Insert(int &x,int pre,int l,int r,int q){
    x=++cnt;c[x]=c[pre];
    if(l==r){sum(x)+=1;return;}
    int mid=(l+r)>>1;
    if(mid>=q)  Insert(ls(x),ls(pre),l,mid,q);
    else Insert(rs(x),rs(pre),mid+1,r,q);
    Push_up(x);return;
}

inline int Query(int u,int v,int lca,int falca,int l,int r,int k){
    if(l==r)    return b[l];
    int mid=(l+r)>>1,tep=sum(ls(u))+sum(ls(v))-sum(ls(lca))-sum(ls(falca));
    if(tep>=k)  return Query(ls(u),ls(v),ls(lca),ls(falca),l,mid,k);
    else return Query(rs(u),rs(v),rs(lca),rs(falca),mid+1,r,k-tep);
}

//Tree Chain Partition
inline void Dfs1(int x,int f){
    fa[x]=f,siz[x]=1,dep[x]=dep[f]+1;
    for(register int i=head[x];i;i=Next[i]){
        int y=to[i];
        if(y==f)   continue;
        Dfs1(y,x);siz[x]+=siz[y];
        if(siz[y]>siz[son[x]])  son[x]=y;
    }
    return;
}

inline void Dfs2(int x){
    Insert(rt[x],rt[fa[x]],1,len,a[x]);
    top[x]=(son[fa[x]]==x ? top[fa[x]] : x);
    if(son[x])  Dfs2(son[x]);
    for(register int i=head[x];i;i=Next[i]){
        int y=to[i];
        if(y==fa[x]||y==son[x]) continue;
        Dfs2(y);
    }
    return;
}

inline int LCA(int u,int v){
	while(top[u]!=top[v]){
		if(dep[top[u]]>dep[top[v]])	u=fa[top[u]];
		else v=fa[top[v]];
	}
	return dep[u] > dep[v] ? v : u;
}

int main(){
    // system("fc my.out P2633_1.out");
    // freopen("P2633_1.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(n),read(m);
    for(register int i=1;i<=n;++i)  read(a[i]),b[i]=a[i];
    sort(b+1,b+n+1);len=unique(b+1,b+n+1)-(b+1);
    for(register int i=1;i<=n;++i)  a[i]=lower_bound(b+1,b+len+1,a[i])-b;
    Build(rt[0],1,len);
    for(register int i=1;i<n;++i){
        int u,v;read(u),read(v);
        Add_edge(u,v),Add_edge(v,u);
    }
    Dfs1(1,0);Dfs2(1);int last=0;
    while(m--){
        int u,v,k;
        read(u),read(v),read(k);u^=last;
        assert(u<=n);
        int lca=LCA(u,v);
        last=Query(rt[u],rt[v],rt[lca],rt[fa[lca]],1,len,k);
        print(last),putchar('\n');
    }
    return 0;
}

posted @ 2021-04-20 17:27  NuoCarter  阅读(38)  评论(0编辑  收藏  举报