主席树
Posted shixinyi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了主席树相关的知识,希望对你有一定的参考价值。
ctsc的D2T1(主席树模板题),大家都半个小时AC了,我因为一个sb bug调了2个多小时……
博主是个大sb。
一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整。
给你一个长度为n的序列s。回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。
其中a<b<c<d。位置也从0开始标号。强制在线。
如果询问只有一个,我们当然可以二分答案,把$\geq mid$的置为1,其他置为-1,
然后就是求满足左端点在$[a,b]$之间,右端点在$[c,d]$之间的最大权值的子序列的权值是否$\geq 0$
那么对于$[b,c]$之间的所有数,是肯定要选的,那么$[a,b-1]$的最大后缀、$[b,c]$、$[c+1,d]$的最大前缀拼起来就是答案
可以用线段树
对于多组询问,我们肯定不能每次二分一个答案就把所有点权都重置一遍
所以就用主席树,第$i$棵树是二分答案的$mid=i$时查找的线段树,就是$<i$的点权都是-1
//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> #include<vector> using namespace std; #define ll long long #define db double #define For(i,a,b) for(int i=(a);i<=(b);++i) #define Rep(i,a,b) for(int i=(a);i>=(b);--i) #define lc son[pos][0] #define rc son[pos][1] const int maxn=2e4+7,maxm=1e7+7; int n,m,a[maxn],p[maxn],TOT,tot,ans; vector<int> G[maxn]; char cc;ll ff; template<typename T>void read(T& aa) { aa=0;ff=1; cc=getchar(); while(cc!=‘-‘&&(cc<‘0‘||cc>‘9‘)) cc=getchar(); if(cc==‘-‘) ff=-1,cc=getchar(); while(cc>=‘0‘&&cc<=‘9‘) aa=aa*10+cc-‘0‘,cc=getchar(); aa*=ff; } struct Node{ int ld,rd,sum; Node(){} Node(int ld,int rd,int sum):ld(ld),rd(rd),sum(sum){} Node operator + (const Node& b) { Node o; o.ld=max(ld,sum+b.ld); o.rd=max(b.rd,rd+b.sum); o.sum=sum+b.sum; return o; } }node[maxm]; int son[maxm][2]; int ql,qr,qx; void get_bld(int pos,int l,int r) { if(l==r) { node[pos]=Node(1,1,1); return; } int mid=(l+r)>>1; get_bld(lc=++tot,l,mid); get_bld(rc=++tot,mid+1,r); node[pos]=node[lc]+node[rc]; } void bld(int& pos,int last,int l,int r) { if(!pos) { pos=++tot; lc=son[last][0]; rc=son[last][1]; } if(l==r) { node[pos]=Node(0,0,-1); return; } int mid=(l+r)>>1; if(qx<=mid) { if(lc==son[last][0]) lc=0; bld(lc,son[last][0],l,mid); } else { if(rc==son[last][1]) rc=0; bld(rc,son[last][1],mid+1,r); } node[pos]=node[lc]+node[rc]; } Node q(int pos,int l,int r) { if(l>=ql&&r<=qr) return node[pos]; int mid=(l+r)>>1; if(qr<=mid) return q(lc,l,mid); if(ql>mid) return q(rc,mid+1,r); return q(lc,l,mid)+q(rc,mid+1,r); } bool check(int x,int l1,int r1,int l2,int r2) { Node L,O,R; L=R=O=Node(0,0,0); ql=l1; qr=r1-1; L=q(x,1,n); ql=l2+1; qr=r2; R=q(x,1,n); ql=r1; qr=l2; O=q(x,1,n); return L.rd+O.sum+R.ld>=0; } int get_ans(int x,int y,int z,int w) { if(x>y) swap(x,y); if(y>z) swap(y,z); if(z>w) swap(z,w); if(x>y) swap(x,y); if(y>z) swap(y,z); if(x>y) swap(x,y); int l1=x,r1=y,l2=z,r2=w; // printf("get_ans:%d~%d,%d~%d\n",l1,r1,l2,r2); int l=1,r=TOT,mid; if(check(r,l1,r1,l2,r2)) return r; while(l<r-1) { mid=(l+r)>>1; if(check(mid,l1,r1,l2,r2)) l=mid; else r=mid; } return l; } int main() { read(n); int x,y,z,w; For(i,1,n) read(a[i]),p[i]=a[i]; sort(p+1,p+n+1); TOT=unique(p+1,p+n+1)-(p+1); For(i,1,n) a[i]=lower_bound(p+1,p+TOT+1,a[i])-p; For(i,1,n) G[a[i]].push_back(i); tot=TOT; get_bld(1,1,n); For(i,2,TOT) { son[i][0]=son[i-1][0]; son[i][1]=son[i-1][1]; node[i]=node[i-1]; x=G[i-1].size(); For(j,0,x-1) { qx=G[i-1][j]; bld(i,i-1,1,n); } } read(m); For(i,1,m) { read(x); read(y); read(z); read(w); x=(x+ans)%n+1; y=(y+ans)%n+1; z=(z+ans)%n+1; w=(w+ans)%n+1; ans=get_ans(x,y,z,w); printf("%d\n",ans=p[ans]); } return 0; }
给一个长度为$n$的序列$a$。$1 \leq a[i] \leq n$。
$m$组询问,每次询问一个区间$[l,r]$,是否存在一个数在$[l,r]$中出现的次数大于$(r-l+1)/2$。如果存在,输出这个数,否则输出0。
一个数,如果满足条件,那么他一定是中位数,所以直接找区间的中位数,然后再查询它出现次数
//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; #define ll long long #define db double #define For(i,a,b) for(int i=(a);i<=(b);++i) #define Rep(i,a,b) for(int i=(a);i>=(b);--i) #define lc son[pos][0] #define rc son[pos][1] const int maxn=5e5+7,maxm=1e7+7; int n,m,tot; char cc;ll ff; template<typename T>void read(T& aa) { aa=0;ff=1; cc=getchar(); while(cc!=‘-‘&&(cc<‘0‘||cc>‘9‘)) cc=getchar(); if(cc==‘-‘) ff=-1,cc=getchar(); while(cc>=‘0‘&&cc<=‘9‘) aa=aa*10+cc-‘0‘,cc=getchar(); aa*=ff; } int sum[maxm],son[maxm][2],qx,qy; void bld(int pos,int last,int l,int r) { sum[pos]=sum[last]+1; if(l==r) return; int mid=(l+r)>>1; if(qx<=mid) rc=son[last][1],bld(lc=++tot,son[last][0],l,mid); else lc=son[last][0],bld(rc=++tot,son[last][1],mid+1,r); } int q(int ld,int rd,int l,int r) { if(l==r) { if(sum[rd]-sum[ld]>=qy) return l; return 0; } int mid=(l+r)>>1,x=sum[son[rd][0]]-sum[son[ld][0]]; if(x>=qx) return q(son[ld][0],son[rd][0],l,mid); qx-=x; return q(son[ld][1],son[rd][1],mid+1,r); } int main() { read(n); read(m); tot=n; For(i,1,n) { read(qx); bld(i,i-1,1,n); } int x,y; For(i,1,m) { read(x); read(y); qx=qy=(y-x+1)/2+1; printf("%d\n",q(x-1,y,1,n)); } return 0; }
有一个长度为n的数组{a1,a2,...,an}。m次询问,每次询问一个区间内最小没有出现过的自然数。
$n,m \leq 2*10^5$
假如我们二分答案,我们就要求区间内有没有$<mid$的没有出现的数,我们就可以……
这样是两个log的
但是实际上这道题我们可以一个log搞定
第$i$棵线段树(权值线段树),表示的是我们如果只考虑数组的前i个数,那么每个数$x$出现的最大位置$f(x)$是在哪,维护区间最小值
对于询问$(l,r)$,我们在第$r$棵线段树上二分,找到最大的$p$,使得$min(f(1),f(2),...,f(p-1)) \geq l$
//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; #define ll long long #define db double #define For(i,a,b) for(int i=(a);i<=(b);++i) #define Rep(i,a,b) for(int i=(a);i>=(b);--i) #define lc son[pos][0] #define rc son[pos][1] const int maxn=2e5+7,maxm=1e7+7; int n,m,W,tot; char cc;ll ff; template<typename T>void read(T& aa) { aa=0;ff=1; cc=getchar(); while(cc!=‘-‘&&(cc<‘0‘||cc>‘9‘)) cc=getchar(); if(cc==‘-‘) ff=-1,cc=getchar(); while(cc>=‘0‘&&cc<=‘9‘) aa=aa*10+cc-‘0‘,cc=getchar(); aa*=ff; } int num[maxm],son[maxm][2],ql,qr,qx; void bld(int pos,int last,int l,int r) { if(l==r) {num[pos]=qx;return;} int mid=(l+r)>>1; if(ql<=mid) rc=son[last][1],bld(lc=++tot,son[last][0],l,mid); else lc=son[last][0],bld(rc=++tot,son[last][1],mid+1,r); num[pos]=min(num[lc],num[rc]); } int q(int pos,int l,int r) { if(l==r) return l; int mid=(l+r)>>1; if(num[lc]<qx) return q(lc,l,mid); return q(rc,mid+1,r); } int main() { read(n); read(m); W=n+1; tot=n; int x,y; For(i,1,n) { read(x); ++x; if(x>n) { son[i][0]=son[i-1][0]; son[i][1]=son[i-1][1]; continue; } ql=qr=x; qx=i; bld(i,i-1,1,W); } For(i,1,m) { read(x); read(y); qx=x; printf("%d\n",q(y,1,W)-1); } return 0; }
以上是关于主席树的主要内容,如果未能解决你的问题,请参考以下文章