可持久化线段树 区间第k大/小
最重要的启示:
1.一定要对拍
2.如果错了顶多调3分钟,调不出来赶紧重写
事实证明:重构代码比调试效率高得多!!!
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1175
一个长度为N的整数序列,编号0 - N - 1。进行Q次查询,查询编号i至j的所有数中,第K大的数是多少。
https://www.luogu.org/problemnew/show/P3834
洛谷P3834 模板题 第K小
http://codevs.cn/problem/2021/
codevs2021 区间中位数 (也就是第(R-L+2 >>1)小。。)
#include<cstdio> #include<cstring> #include<algorithm> #include<cctype> using namespace std; inline int gi(){ int d=0,f=1;char c=getchar();while(!isdigit(c)){ if(c=='-')f=-1;c=getchar(); }while(isdigit(c)){d=(d<<3)+(d<<1)+c-'0';c=getchar(); }return d*f; } const int N=200005; const int M=6000005; int rs[M],ls[M],sm[M],rt[M],tot; #define gem int mi=(l+r)>>1 void gai(int old,int &o,int l,int r,int P,int C){ o=++tot; sm[o]=sm[old]+C; ls[o]=ls[old];rs[o]=rs[old]; if(l==r)return; gem; if(P<=mi)gai(ls[o],ls[o],l,mi,P,C); else gai(rs[o],rs[o],mi+1,r,P,C); } int qur(int old,int o,int l,int r,int k){ if(l==r)return l; int sum=sm[ls[o]]-sm[ls[old]];gem; if(sum>=k)return qur(ls[old],ls[o],l,mi,k); else return qur(rs[old],rs[o],mi+1,r,k-sum); } int a[N],san[N],num[N],cnt; #define rep(xx,yy,zz) for(xx=yy;xx<=zz;++xx) int ans,n,Q,x,L,R,K; int main(){ register int i; n=gi();Q=gi(); rep(i,1,n){ a[i]=gi();san[i]=a[i]; } sort(san+1,san+n+1); rep(i,1,n) if(i==1||san[i]!=san[i-1]) num[++cnt]=san[i]; rep(i,1,n){ x=lower_bound(num+1,num+cnt+1,a[i])-num; gai(rt[i-1],rt[i],1,cnt,x,1); } rep(i,1,Q){ L=gi();R=gi();K=(R-L+2)>>1; ans=qur(rt[L-1],rt[R],1,cnt,K); printf("%d\n",num[ans]); } return 0; }