【题解】Query on a tree III [SP1487] [Bzoj1803]

【题解】Query on a tree III [SP1487] [Bzoj1803]

传送门:\(\text{Query on a tree III [SP1487]}\) \(\text{[Bzoj1803]}\)

【题目描述】

给出一棵 \(n\) \((n \leqslant 10^5)\) 个节点的树,每个点都带有点权,一共 \(m\) \((m \leqslant 10^4)\) 个询问,每次查询以 \(x\) 为根的子树内权值第 \(k\) 小的节点编号。

【分析】

发现网上所有题解都是主席树,我线段树合并表示不服!

明明就是树上线段树合并的板子,居然没一个人写。。。

查询若干个子树的相关信息且不强制在线,可以先用链表把询问挂到 \(x\) 上,初始化对每个节点开一棵,从下往上逐步合并,每合并完一棵子树就把链表掏出来查询一波信息,然后再继续往上合并。

由于节点权值两两不同,所以离散化后映射一下每个权值所在的节点编号即可。

时间复杂度:\(O((n+m)logn)\)

【Code】

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
#define LL long long
#define Re register int
using namespace std;
const int N=1e5+3;
int n,m,o,x,y,T,b[N],A[N],ID[N],Ans[N],head[N];
struct QAQ{int to,next;}a[N<<1];
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
inline void in(Re &x){
    Re fu=0;x=0;char ch=getchar();
    while(ch<'0'||ch>'9')fu|=ch=='-',ch=getchar();
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    x=fu?-x:x;
}
struct QWQ{//用链表挂询问
    int o,head[N];
    struct QAQ{int k,id,next;}a[N<<1];
    inline void add(Re x,Re k,Re id){a[++o].k=k,a[o].id=id,a[o].next=head[x],head[x]=o;}
}T0;
struct Segment_Tree{
    #define pl tr[p].lp
    #define pr tr[p].rp
    #define mid (L+R>>1)
    int O,pt[N];
    struct QAQ{int S,lp,rp;}tr[N*18];
    inline void change(Re &p,Re L,Re R,Re x){//单点修改
        if(!p)p=++O;++tr[p].S;
        if(L==R)return;
        if(x<=mid)change(pl,L,mid,x);
        else change(pr,mid+1,R,x);
    }
    inline void build(){for(Re i=1;i<=n;++i)change(pt[i],1,m,A[i]);}//初始化建立n棵权值线段树
    inline int merge(Re p,Re q){//线段树合并
        if(!p)return q;if(!q)return p;
        tr[p].S+=tr[q].S;
        pl=merge(pl,tr[q].lp);
        pr=merge(pr,tr[q].rp);
        return p;
    }
    inline int ask(Re p,Re L,Re R,Re k){//查询第k小
        if(L==R)return ID[L];
        Re tmp=tr[pl].S;
        if(tmp>=k)return ask(pl,L,mid,k);
        else return ask(pr,mid+1,R,k-tmp);
    }
}T1;
inline void dfs(Re x,Re fa){
    for(Re i=head[x],to;i;i=a[i].next)
        if((to=a[i].to)!=fa)dfs(to,x),T1.pt[x]=T1.merge(T1.pt[x],T1.pt[to]);//合并儿子
    for(Re i=T0.head[x];i;i=T0.a[i].next)Ans[T0.a[i].id]=T1.ask(T1.pt[x],1,m,T0.a[i].k);//把x节点上挂着的询问都拿出来跑一下
}
int main(){
//  freopen("123.txt","r",stdin);
    in(n),m=n-1;
    for(Re i=1;i<=n;++i)in(A[i]),b[i]=A[i];
    while(m--)in(x),in(y),add(x,y),add(y,x);
    sort(b+1,b+n+1),m=unique(b+1,b+n+1)-b-1;//离散化
    for(Re i=1;i<=n;++i)A[i]=lower_bound(b+1,b+m+1,A[i])-b,ID[A[i]]=i;
    T1.build(),in(T);
    for(Re i=1;i<=T;++i)in(x),in(y),T0.add(x,y,i);//把询问挂到链表上
    dfs(1,0);
    for(Re i=1;i<=T;++i)printf("%d\n",Ans[i]);
}

另附主席树代码:

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
#define LL long long
#define Re register int
using namespace std;
const int N=1e5+3;
int n,m,o,x,y,T,dfn_o,b[N],A[N],AA[N],ID[N],dfn[N],size[N],head[N];
struct QAQ{int to,next;}a[N<<1];
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
inline void in(Re &x){
    Re fu=0;x=0;char ch=getchar();
    while(ch<'0'||ch>'9')fu|=ch=='-',ch=getchar();
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    x=fu?-x:x;
}
struct Segment_Tree{
    #define pl tr[p].lp
    #define pr tr[p].rp
    #define mid (L+R>>1)
    int O,pt[N];
    struct QAQ{int S,lp,rp;}tr[N*18];
    inline void creat(Re &p,Re q,Re L,Re R,Re x){
        tr[p=++O]=tr[q],++tr[p].S;
        if(L==R)return;
        if(x<=mid)creat(pl,tr[q].lp,L,mid,x);
        else creat(pr,tr[q].rp,mid+1,R,x);
    }
    inline int ask(Re p,Re q,Re L,Re R,Re k){//查询区间第k小
        if(L==R)return ID[L];
        Re tmp=tr[tr[q].lp].S-tr[pl].S;
        if(tmp>=k)return ask(pl,tr[q].lp,L,mid,k);
        else return ask(pr,tr[q].rp,mid+1,R,k-tmp);
    }
}T1;
inline void dfs(Re x,Re fa){
    AA[dfn[x]=++dfn_o]=A[x],size[x]=1;
    for(Re i=head[x],to;i;i=a[i].next)
        if((to=a[i].to)!=fa)dfs(to,x),size[x]+=size[to];
}
int main(){
//  freopen("123.txt","r",stdin);
    in(n),m=n-1;
    for(Re i=1;i<=n;++i)in(A[i]),b[i]=A[i];
    while(m--)in(x),in(y),add(x,y),add(y,x);
    sort(b+1,b+n+1),m=unique(b+1,b+n+1)-b-1;//离散化
    for(Re i=1;i<=n;++i)A[i]=lower_bound(b+1,b+m+1,A[i])-b,ID[A[i]]=i;
    dfs(1,0);
    for(Re i=1;i<=n;++i)T1.creat(T1.pt[i],T1.pt[i-1],1,m,AA[i]);//建主席树
    in(T);
    while(T--)in(x),in(y),printf("%d\n",T1.ask(T1.pt[dfn[x]-1],T1.pt[dfn[x]+size[x]-1],1,m,y));
    
}
posted @ 2020-01-18 19:32  辰星凌  阅读(177)  评论(0编辑  收藏  举报