标签:lin line space stdin 信息 描述 bzoj 就是 string
传送门:\(\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)\) 。
#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));
}
【题解】Query on a tree III [SP1487] [Bzoj1803]
标签:lin line space stdin 信息 描述 bzoj 就是 string
原文地址:https://www.cnblogs.com/Xing-Ling/p/12209965.html