BZOJ1803: Spoj1487 Query on a tree III
【传送门:BZOJ1803】
简要题意:
给出一棵有根有点权的n个点的树,有m个询问,每个询问输入x,k,输出以x为根的子树内的第k小值
题解:
树上主席树裸题
求一遍DFS序,然后因为每棵子树的序号是连续的,所以只要记录以x为根的子树的序号区间就可以了
然后利用前缀和思想求第k小值就行了
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; struct trnode { int lc,rc,c; }tr[2100000];int trlen,rt[110000]; void Link(int &u,int l,int r,int p) { if(u==0) u=++trlen; tr[u].c++; if(l==r) return ; int mid=(l+r)/2; if(p<=mid) Link(tr[u].lc,l,mid,p); else Link(tr[u].rc,mid+1,r,p); } void Merge(int &u1,int u2) { if(u1==0){u1=u2;return ;} if(u2==0) return ; tr[u1].c+=tr[u2].c; Merge(tr[u1].lc,tr[u2].lc); Merge(tr[u1].rc,tr[u2].rc); } int n; int s[110000],ls[110000]; int LS(int d) { int l=1,r=n,ans; while(l<=r) { int mid=(l+r)/2; if(ls[mid]<=d) { ans=mid; l=mid+1; } else r=mid-1; } return ans; } int t[110000]; int findkth(int u1,int u2,int l,int r,int k) { if(l==r) return t[l]; int c=tr[tr[u1].lc].c-tr[tr[u2].lc].c,mid=(l+r)/2; if(k<=c) return findkth(tr[u1].lc,tr[u2].lc,l,mid,k); else return findkth(tr[u1].rc,tr[u2].rc,mid+1,r,k-c); } struct node { int x,y,next; }a[210000];int len,last[110000]; void ins(int x,int y) { len++; a[len].x=x;a[len].y=y; a[len].next=last[x];last[x]=len; } int l[110000],r[110000],z,ys[110000],sy[110000]; void dfs(int x,int fa) { l[x]=++z;ys[x]=z;sy[z]=x; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y!=fa) { dfs(y,x); } } r[x]=z; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&s[i]),ls[i]=s[i]; sort(ls+1,ls+n+1); len=0;memset(last,0,sizeof(last)); for(int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); ins(x,y);ins(y,x); } z=0;dfs(1,0); trlen=0;memset(rt,0,sizeof(rt)); for(int i=1;i<=n;i++) { int w=LS(s[sy[i]]); t[w]=sy[i]; Link(rt[i],1,n,w); Merge(rt[i],rt[i-1]); } int m; scanf("%d",&m); for(int i=1;i<=m;i++) { int x,k; scanf("%d%d",&x,&k); printf("%d\n",findkth(rt[r[x]],rt[l[x]-1],1,n,k)); } return 0; }
渺渺时空,茫茫人海,与君相遇,幸甚幸甚