标签:div nlog efi 前缀 getc 之间 \n col 出现
就是板子、、、
节点中维护的值,就是1-i之间这个区间内出现了数的次数(权值线段树?雾)。然后当我们查询的时候,就是利用到了前缀和的思想,拿左端点那棵树和右端点一减~
更新的时候需要新开的点就开,不需要的就连到原来的点上去,相当于更新一条链。这样复杂度是nlogn的。
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #define R register int #define ls (tr<<1) #define rs (tr<<1|1) using namespace std; inline int g() { R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch==‘-‘?-1:fix; do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix; } const int N=200010; int n,m,q,tot,T; int a[N],b[N]; int t[N],sum[N<<5],L[N<<5],Rr[N<<5]; inline int build(int l,int r) { R tr=++tot,md=(l+r)>>1; sum[tr]=0; if(l<r) L[tr]=build(l,md),Rr[tr]=build(md+1,r); return tr; } inline int upd(int pre,int l,int r,int vl) { R tr=++tot; R md=(l+r)>>1; L[tr]=L[pre],Rr[tr]=Rr[pre],sum[tr]=sum[pre]+1; if(l<r) { if(vl<md+1) L[tr]=upd(L[pre],l,md,vl); else Rr[tr]=upd(Rr[pre],md+1,r,vl); } return tr; } inline int query(int lst,int tr,int l,int r,int k) { if(l>=r) return l; R md=(l+r)>>1; R x=sum[L[tr]]-sum[L[lst]]; if(x>=k) return query(L[lst],L[tr],l,md,k); else return query(Rr[lst],Rr[tr],md+1,r,k-x); } signed main() { n=g(),q=g(); for(R i=1;i<=n;++i) a[i]=b[i]=g(); sort(b+1,b+n+1); R m=unique(b+1,b+n+1)-b-1; t[0]=build(1,m); for(R i=1;i<=n;++i) { a[i]=lower_bound(b+1,b+m+1,a[i])-b; t[i]=upd(t[i-1],1,m,a[i]); } while(q--) { R x=g(),y=g(),z=g(); R p=query(t[x-1],t[y],1,m,z); printf("%d\n",b[p]); } }
好想大佬们都把根作为实参转进去。。我的码风有待提高。。QAQ
2019.04.17
Luogu P3834 【模板】可持久化线段树 1(主席树)
标签:div nlog efi 前缀 getc 之间 \n col 出现
原文地址:https://www.cnblogs.com/Jackpei/p/10727096.html