题意:给定一颗树。询问一个子树中第k大的节点。
【终于在0:02的时候A掉了=。=】
这几天复习一下数据结构。先复习POJ2104。写可持久化线段树练手先。
然后到了这道题。其实也蛮裸的。
先求出dfs序。记录每个节点开始和结束的时间戳。然后就相当于每个点开始到结束这个区间里求第2*k大。
然后就是一个主席树了。
根据JC大神的指点结束的时间戳不用占位置了,根据下一个开始的时间戳减一就行。这样可以省一半的空间,也可以省很多时间。
由于spoj太慢加了一个read。后来一不小心就A了。速度还行吧。。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int w[200010],lc[2100010],rc[4100010],bin[100010],p[200010],n1[200010],h[100010],ee=0,tot; int id[100010],sum[2100010],T[100010],num[100010],begin[100010],end[100010],n,c=0; void read(int &x){ char ch; for(ch=getchar();ch<'0'||ch>'9';ch=getchar()); x=ch-'0'; for(ch=getchar();ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-48; }inline void ae(int a,int b){ p[ee]=b; n1[ee]=h[a]; h[a]=ee++; p[ee]=a; n1[ee]=h[b]; h[b]=ee++; }void dfs(int u,int fa){ num[begin[u]=++c]=w[u]; for(int i=h[u];~i;i=n1[i]) if(p[i]!=fa) dfs(p[i],u); end[u]=c; }void build(int l,int r,int &rt){ rt=++tot; sum[rt]=0; if(l==r) return; int m=l+r>>1; build(l,m,lc[rt]); build(m+1,r,rc[rt]); }void modify(int p,int v,int l,int r,int &rt){ rt=++tot; sum[rt]=sum[p]+1; lc[rt]=lc[p]; rc[rt]=rc[p]; if(l==r) return; int m=l+r>>1; if(v<=m) modify(lc[p],v,l,m,lc[rt]); else modify(rc[p],v,m+1,r,rc[rt]); }int ask(int L,int R,int l,int r,int k){ if(l==r) return l; int cnt=sum[lc[R]]-sum[lc[L]],m=l+r>>1; if(k<=cnt) return ask(lc[L],lc[R],l,m,k); else return ask(rc[L],rc[R],m+1,r,k-cnt); }int main(){ read(n); memset(h,-1,sizeof(h)); for(int i=1;i<=n;i++) read(w[i]),bin[i]=w[i]; sort(bin+1,bin+n+1); int x,y; for(int i=1;i<=n;i++){ w[i]=lower_bound(bin+1,bin+n+1,w[i])-bin;id[w[i]]=i;} for(int i=1;i<n;i++) read(x),read(y),ae(x,y); dfs(1,0); build(1,n,T[0]); for(int i=1;i<=n;i++) modify(T[i-1],num[i],1,n,T[i]); int q; read(q); while(q--){ read(x);read(y); printf("%d\n",id[ask(T[begin[x]-1],T[end[x]],1,n,y)]); }return 0; }