【模板整合计划】高阶数据结构—线段树
一:【基本操作及扩展】
1.【区间修改(+),区间查询(Sum)】
【模板】线段树 \(1\) \(\text{[P3372]}\)
#include<cstdio>
#define Re register int
#define LL long long
#define pl p<<1
#define pr p<<1|1
const int N=1e5+3;
struct QAQ{int l,r;LL S,add;}Q[N<<2];
int i,b,c,d,e,n,m,fu,a[N];
inline void creat(Re p,Re l,Re r){
Q[p].l=l,Q[p].r=r;
if(l==r){Q[p].S=a[l];return;}
Re mid=l+r>>1;
creat(pl,l,mid),creat(pr,mid+1,r);
Q[p].S=Q[pl].S+Q[pr].S;
}
inline void spread(Re p){
if(Q[p].add){
LL a=Q[p].add;
Q[pl].S+=a*(Q[pl].r-Q[pl].l+1);
Q[pr].S+=a*(Q[pr].r-Q[pr].l+1);
Q[pl].add+=a,Q[pr].add+=a,Q[p].add=0;
}
}
inline void change(Re p,Re l,Re r,Re x){
Re L=Q[p].l,R=Q[p].r;
if(l<=L&&R<=r){Q[p].S+=(LL)x*(R-L+1),Q[p].add+=x;return;}
Re mid=L+R>>1;spread(p);
if(l<=mid)change(pl,l,r,x);
if(r>mid)change(pr,l,r,x);
Q[p].S=Q[pl].S+Q[pr].S;
}
inline LL ask(Re p,Re l,Re r){
Re L=Q[p].l,R=Q[p].r;
if(l<=L&&R<=r)return Q[p].S;
Re mid=L+R>>1;LL ans=0;spread(p);
if(l<=mid)ans+=ask(pl,l,r);
if(r>mid)ans+=ask(pr,l,r);
return ans;
}
inline void in(int &x){
x=fu=0;char c=getchar();
while(c<'0'||c>'9')fu|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
if(fu)x=-x;
}
int main(){
in(n),in(m);
for(i=1;i<=n;i++)in(a[i]);
creat(1,1,n);
while(m--){
in(b),in(c),in(d);
if(b>1)printf("%lld\n",ask(1,c,d));
else in(e),change(1,c,d,e);
}
}
2.【区间修改(+,×),区间查询(Sum)】
【模板】线段树 2 \(\text{[P3373]}\) / 维护序列 \(\text{[P2023]}\) \(\text{[BZOJ1798]}\)**
#include<cstdio>
#define LL long long
#define Re register LL
#define pl p<<1
#define pr p<<1|1
const int N=1e5+3;
struct QAQ{LL l,r,S,add,mul;}Q[N<<2];
LL i,b,c,d,e,n,m,P,fu,a[N];
inline void creat(Re p,Re l,Re r){
Q[p].l=l,Q[p].r=r,Q[p].mul=1;
if(l==r){Q[p].S=a[l]%P;return;}
Re mid=l+r>>1;
creat(pl,l,mid),creat(pr,mid+1,r);
Q[p].S=(Q[pl].S+Q[pr].S)%P;
}
inline void spread(Re p){
Re a=Q[p].add,m=Q[p].mul;
((Q[pl].S*=m)+=a*(Q[pl].r-Q[pl].l+1))%=P;
((Q[pr].S*=m)+=a*(Q[pr].r-Q[pr].l+1))%=P;
(Q[pl].mul*=m)%=P,(Q[pr].mul*=m)%=P;
((Q[pl].add*=m)+=a)%=P,((Q[pr].add*=m)+=a)%=P;
Q[p].add=0;Q[p].mul=1;
}
inline void change1(Re p,Re l,Re r,Re x){
Re L=Q[p].l,R=Q[p].r;
if(l<=L&&R<=r){(Q[p].S+=x*(R-L+1))%=P,(Q[p].add+=x)%=P;return;}
Re mid=L+R>>1;spread(p);
if(l<=mid)change1(pl,l,r,x);
if(r>mid)change1(pr,l,r,x);
Q[p].S=(Q[pl].S+Q[pr].S)%P;
}
inline void change2(Re p,Re l,Re r,Re x){
Re L=Q[p].l,R=Q[p].r;
if(l<=L&&R<=r){(Q[p].S*=x)%=P,(Q[p].add*=x)%=P,(Q[p].mul*=x)%=P;return;}
Re mid=L+R>>1;spread(p);
if(l<=mid)change2(pl,l,r,x);
if(r>mid)change2(pr,l,r,x);
Q[p].S=(Q[pl].S+Q[pr].S)%P;
}
inline LL ask(Re p,Re l,Re r){
Re L=Q[p].l,R=Q[p].r;
if(l<=L&&R<=r)return Q[p].S;
Re mid=L+R>>1,ans=0;spread(p);
if(l<=mid)(ans+=ask(pl,l,r))%=P;
if(r>mid)(ans+=ask(pr,l,r))%=P;
return ans;
}
inline void in(Re &x){
x=fu=0;char c=getchar();
while(c<'0'||c>'9')fu|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
if(fu)x=-x;
}
int main(){
in(n),in(m),in(P);
for(i=1;i<=n;i++)in(a[i]);
creat(1,1,n);
while(m--){
in(b),in(c),in(d);
if(b>2)printf("%lld\n",ask(1,c,d));
else{
in(e);
if(b>1)change1(1,c,d,e%P);
else change2(1,c,d,e%P);
}
}
}
3.【区间修改(+,/),区间查询(Sum,min)】
「雅礼集训 \(2017\) \(\text{Day1}\)」市场 \(\text{[Loj6029]}\)
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define Re register LL
#define pl p<<1
#define pr p<<1|1
#define div(a,b) floor((double)a/b)
const int N=1e5+3;
struct QAQ{LL l,r,ma,mi,S,add;}Q[N<<2];
LL T,i,b,c,d,n,m,fu,a[N];
inline void in(Re &x){
x=fu=0;char c=getchar();
while(c<'0'||c>'9')fu|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
if(fu)x=-x;
}
inline LL max(Re a,Re b){return a>b?a:b;}
inline LL min(Re a,Re b){return a<b?a:b;}
inline void down(Re p){
Re adds=Q[p].add;
if(adds){
Q[pl].add+=adds,Q[pr].add+=adds;
Q[pl].ma+=adds,Q[pr].ma+=adds;
Q[pl].mi+=adds,Q[pr].mi+=adds;
Q[pl].S+=adds*(Q[pl].r-Q[pl].l+1),Q[pr].S+=adds*(Q[pr].r-Q[pr].l+1);
Q[p].add=0;
}
}
inline void up(Re p){
Q[p].S=Q[pl].S+Q[pr].S;
Q[p].ma=max(Q[pl].ma,Q[pr].ma);
Q[p].mi=min(Q[pl].mi,Q[pr].mi);
}
inline void creat(Re p,Re l,Re r){
Q[p].l=l,Q[p].r=r;
if(l==r){Q[p].ma=Q[p].mi=Q[p].S=a[l];return;}
Re mid=l+r>>1;
creat(pl,l,mid),creat(pr,mid+1,r);
up(p);
}
inline void plus_(Re p,Re l,Re r,Re x){
Re L=Q[p].l,R=Q[p].r,mid=L+R>>1;
if(l<=L&&R<=r){Q[p].S+=x*(R-L+1),Q[p].ma+=x,Q[p].mi+=x,Q[p].add+=x;return;}
down(p);
if(l<=mid)plus_(pl,l,r,x);
if(r>mid)plus_(pr,l,r,x);
up(p);
}
inline void div_(Re p,Re l,Re r,Re d){
Re L=Q[p].l,R=Q[p].r,mid=L+R>>1;
if(l<=L&&R<=r&&(Q[p].ma-div(Q[p].ma,d)==Q[p].mi-div(Q[p].mi,d))){
Re c=div(Q[p].ma,d)-Q[p].ma;
Q[p].S+=c*(R-L+1),Q[p].add+=c,Q[p].ma+=c,Q[p].mi+=c;
return;
}
down(p);
if(l<=mid)div_(pl,l,r,d);
if(r>mid)div_(pr,l,r,d);
up(p);
}
inline LL ask_min(Re p,Re l,Re r){
Re L=Q[p].l,R=Q[p].r;
if(l<=L&&R<=r)return Q[p].mi;
Re mid=L+R>>1,ans=2e9;
down(p);
if(l<=mid)ans=min(ans,ask_min(pl,l,r));
if(r>mid)ans=min(ans,ask_min(pr,l,r));
return ans;
}
inline LL ask_sum(Re p,Re l,Re r){
Re L=Q[p].l,R=Q[p].r;
if(l<=L&&R<=r)return Q[p].S;
Re mid=L+R>>1,ans=0;
down(p);
if(l<=mid)ans+=ask_sum(pl,l,r);
if(r>mid)ans+=ask_sum(pr,l,r);
return ans;
}
int main(){
in(n),in(T);
for(i=1;i<=n;i++)in(a[i]);
creat(1,1,n);
while(T--){
in(b),in(c),in(d),++c,++d;
if(b<2)in(i),plus_(1,c,d,i);
else if(b<3)in(i),div_(1,c,d,i);
else if(b<4)printf("%lld\n",ask_min(1,c,d));
else printf("%lld\n",ask_sum(1,c,d));
}
}
4.【区间修改(sqrt),区间查询(Sum)】
\(\text{GSS4 - Can you answer these queries IV} [SP2713]\)
一个数不断地求 \(sqrt\) 会迅速变为 \(1\),此时不需要再继续开方,暴力单修加上剪枝即可。
#include<cstring>
#include<cstdio>
#include<cmath>
#define Re register int
#define pl p<<1
#define pr p<<1|1
const int N=5e4+3;
struct QAQ{int l,r,ma,S;}Q[N<<2];
int i,b,c,d,n,m,fu,a[N];
inline int max(Re a,Re b){return a>b?a:b;}
inline void creat(Re p,Re l,Re r){
Q[p].l=l,Q[p].r=r;
if(l==r){Q[p].ma=Q[p].S=a[l];return;}
Re mid=l+r>>1;
creat(pl,l,mid),creat(pr,mid+1,r);
Q[p].S=Q[pl].S+Q[pr].S;
Q[p].ma=max(Q[pl].ma,Q[pr].ma);
}
inline void change(Re p,Re l,Re r){
Re L=Q[p].l,R=Q[p].r,mid=L+R>>1;
if(L==R){Q[p].ma=sqrt(Q[p].ma),Q[p].S=sqrt(Q[p].S);return;}
if(l<=mid&&Q[pl].ma>1)change(pl,l,r);
if(r>mid&&Q[pr].ma>1)change(pr,l,r);
Q[p].S=Q[pl].S+Q[pr].S;
Q[p].ma=max(Q[pl].ma,Q[pr].ma);
}
inline int ask(Re p,Re l,Re r){
Re L=Q[p].l,R=Q[p].r;
if(l<=L&&R<=r)return Q[p].S;
Re mid=L+R>>1;int ans=0;
if(l<=mid)ans+=ask(pl,l,r);
if(r>mid)ans+=ask(pr,l,r);
return ans;
}
inline void in(Re &x){
x=fu=0;char c=getchar();
while(c<'0'||c>'9')fu|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
if(fu)x=-x;
}
int main(){
scanf("%d",&n);
memset(Q,0,sizeof(Q));
for(i=1;i<=n;i++)in(a[i]);
creat(1,1,n);
while(n--){
in(b),in(c),in(d),in(i);
if(b)printf("%d\n",ask(1,c,d));
else change(1,c,d);
}
}
5.【区间修改(+),区间查询(最大子段和)】
\(\text{GSS3 - Can you answer these queries III} [SP1716]\)
对每个区间维护最大子序列 \(S\),紧靠左边界的最大子序列 \(ls\),紧靠右边界的最大子序列 \(rs\)。
#include<cstdio>
#define Re register int
#define pl p<<1
#define pr p<<1|1
const int N=5e4+3;
struct QAQ{int l,r,S,ls,rs,ans;}Q[N<<2];
int i,b,c,d,n,m,fu,a[N];
inline int max(int a,int b){return a>b?a:b;}
inline void up(QAQ &P,QAQ L,QAQ R){
P.S=L.S+R.S;
P.ls=max(L.ls,L.S+R.ls);
P.rs=max(R.rs,R.S+L.rs);
P.ans=max(max(L.ans,R.ans),L.rs+R.ls);
}
inline void creat(Re p,Re l,Re r){
Q[p].l=l,Q[p].r=r;
if(l==r){Q[p].ans=Q[p].ls=Q[p].rs=Q[p].S=a[l];return;}
Re mid=l+r>>1;
creat(pl,l,mid),creat(pr,mid+1,r);
up(Q[p],Q[pl],Q[pr]);
}
inline void change(Re p,Re w,Re x){
Re L=Q[p].l,R=Q[p].r,mid=L+R>>1;
if(L==R){Q[p].ans=Q[p].ls=Q[p].rs=Q[p].S=x;return;}
if(w<=mid)change(pl,w,x);
else change(pr,w,x);
up(Q[p],Q[pl],Q[pr]);
}
inline QAQ ask(Re p,Re l,Re r){
Re L=Q[p].l,R=Q[p].r,mid=L+R>>1;
if(l<=L&&R<=r)return Q[p];
if(r<=mid)return ask(pl,l,r);
if(l>mid)return ask(pr,l,r);
QAQ x=ask(pl,l,mid),y=ask(pr,mid+1,r),ans;
up(ans,x,y);
return ans;
}
inline void in(int &x){
x=fu=0;char c=getchar();
while(c<'0'||c>'9')fu|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
if(fu)x=-x;
}
int main(){
in(n);
for(i=1;i<=n;i++)in(a[i]);
creat(1,1,n);in(m);
while(m--){
in(b),in(c),in(d);
if(b)printf("%d\n",ask(1,c,d).ans);
else change(1,c,d);
}
}
6.【区间修改(+),区间查询(GCD)】
\(\text{Interval}\) \(\text{GCD}\) \(\text{[CH4302]}\)
\(gcd(x,y)=gcd(x,y-x)\),
\(gcd(x,y,z)=gcd(x,y-x,z-y)\),
\(...\)
线段树维护差分序列 \(c\),树状数组维护原序列 \(a\),\(gcd(a[l],a[l+1]...a[r])=gcd(a[l],gcd(c[l+1]...c[r]))\)。
#include<cstdio>
#define LL long long
#define Re register LL
#define pl p<<1
#define pr p<<1|1
const int N=5e5+3;
struct QAQ{LL l,r,gcd;}Q[N<<2];
LL i,c,d,e,n,m,fu,C[N],a[N],b[N];char k;
inline LL abs(Re a){return a<0?-a:a;}
inline LL gcd(Re n,Re m){
if(!m)return n;
return n%m?gcd(m,n%m):m;
}
inline void add_c(Re x,Re y){while(x<=n)C[x]+=y,x+=x&(-x);}
inline LL ask_x(Re x){
Re ans=0;
while(x)ans+=C[x],x-=x&(-x);
return ans;
}
inline void creat(Re p,Re l,Re r){
Q[p].l=l,Q[p].r=r;
if(l==r){Q[p].gcd=b[l];return;}
Re mid=l+r>>1;
creat(pl,l,mid),creat(pr,mid+1,r);
Q[p].gcd=gcd(Q[pl].gcd,Q[pr].gcd);
}
inline void change(Re p,Re w,Re x){
Re L=Q[p].l,R=Q[p].r;
if(L==R){Q[p].gcd+=x;return;}
Re mid=L+R>>1;
if(w<=mid)change(pl,w,x);
else change(pr,w,x);
Q[p].gcd=gcd(Q[pl].gcd,Q[pr].gcd);
}
inline LL ask(Re p,Re l,Re r){
Re L=Q[p].l,R=Q[p].r;
if(l<=L&&R<=r)return abs(Q[p].gcd);
Re mid=L+R>>1;
if(r<=mid)return ask(pl,l,r);
if(l>mid)return ask(pr,l,r);
return gcd(ask(pl,l,r),ask(pr,l,r));
}
inline void in(Re &x){
x=fu=0;char c=getchar();
while(c<'0'||c>'9')fu|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
if(fu)x=-x;
}
int main(){
in(n),in(m);
for(i=1;i<=n;i++)in(a[i]),b[i]=a[i]-a[i-1],add_c(i,b[i]);
creat(1,1,n);
while(m--){
scanf(" %c",&k),in(c),in(d);
if(k=='Q')printf("%lld\n",gcd(ask_x(c),ask(1,c+1,d)));
else{
in(e),add_c(c,e),add_c(d+1,-e);
change(1,c,e);
if(d<n)change(1,d+1,-e);
}
}
}
二:【扫描线】
三:【权值线段树区间第 K 小】
#define Re register int
#define pl tree[p].PL
#define pr tree[p].PR
inline int ask(Re p,Re L,Re R,Re k){//查询第k小
if(L==R)return L;//边界叶节点
Re tmp=tree[pl].g;//计算左子树(数值范围在L~mid的数)共有多少个数字
if(tmp>=k)return ask(pl,L,mid,k);
//左子树已经超过k个,说明第k小在左子树里面
else return ask(pr,mid+1,R,k-tmp);
//左子树不足k个数字,应该在右子树中找到第(k-tmp)小
}
四:【动态开点】
#define Re register int
#define pl tree[p].PL
#define pr tree[p].PR
int cnt;
inline void sakura(Re &p,Re L,Re R,Re ???){//【???修改】
if(!p)p=++cnt,tree[p].?=???;
//发现进入了一个空节点,新建一个节点,赋予它编号,记录基本信息
if(L==R){tree[p].?=???;return;}
//达到叶子节点,记录一些特殊的信息,并返回
Re tmp=???;//可能会在在递归之前进行一些计算来方便判断
if(???)sakura(pl,L,mid,???);//递归进入左子树
if(???)sakura(pr,mid+1,R,???);//递归进入右子树
tree[p].?=???;//回溯后更新信息
}
五:【线段树合并】
#define Re register int
#define pl tree[p].PL
#define pr tree[p].PR
inline int merge(Re p,Re q){//【线段树合并】
if(!p)return q;if(!q)return p;
//当需要合并的点的其中一个编号为0时 (即为空),返回另一个编号
tr[p].g+=tr[q].g,p;//把q合并到p上面去
pl=merge(pl,tr[q].lp);//合并左子树,并记录p点的左子树编号
pr=merge(pr,tr[q].rp);//合并右子树,并记录p点的右子树编号
return p;
}
六:【可持续化线段树—静态主席树】
【模板】可持久化线段树 \(1\) (主席树) \(\text{[3834]}\) / \(\text{K-th Number [POJ2104]}\) \(\text{[SP3946]}\)
#include<algorithm>
#include<cstdio>
#define mid (L+R>>1)
#define pl tr[p].lp
#define pr tr[p].rp
#define Re register int
#define F(a,b) for(i=a;i<=b;++i)
using namespace std;
const int N=1e5+3;
int x,y,z,i,n,m,k,t,fu,cnt,a[N],b[N],pt[N];//pt[i]表示离散化后i这个位置所对应的权值树根的编号
struct QAQ{int g,lp,rp;}tr[N<<5];//权值树,保守开一个32*N
inline void in(Re &x){//【快读】自己动手,丰衣足食...
x=fu=0;char c=getchar();
while(c<'0'||c>'9')fu|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();x=fu?-x:x;
}
inline int creat(Re pp,Re x,Re L,Re R){
//把上一棵权值树pp(即pt[a[i-1]])复制过来
//并在递归复制途中对x(即a[i]离散化后的位置)这个点同步进行单修操作
Re p=++cnt;pl=tr[pp].lp,pr=tr[pp].rp,tr[p].g=tr[pp].g+1;
//新开一个点,并把上一个的数据复制进来,并使tr[].g++
if(L==R)return p;//到达边界: L==R(即x这个位置)
if(x<=mid)pl=creat(tr[pp].lp,x,L,mid);//递归进入条件:单修
else pr=creat(tr[pp].rp,x,mid+1,R);//注意tr[pp]要同时递归至左(右)子树
return p;
}
inline int ask(Re p,Re pp,Re L,Re R,Re k){
//查询。p为查询区间左端点的权值树根编号,pp为查询区间右端点的权值树根编号
if(L==R)return b[R];//边界:L==R
Re tmp=tr[tr[pp].lp].g-tr[pl].g;//用前缀和思想计算出左子树共有多少个数字
if(tmp>=k)return ask(pl,tr[pp].lp,L,mid,k);//左子树已经超过k个,说明第k小在左子树里面
else return ask(pr,tr[pp].rp,mid+1,R,k-tmp);//左子树不足k个,应该在右子树中找第(k-tmp)小
}
int main(){
in(n),in(k);
F(1,n)in(a[i]),b[i]=a[i];//复制进b[]并离散去重
sort(b+1,b+n+1);//【离散化】
m=unique(b+1,b+n+1)-b-1;//【去重】
F(1,n)pt[i]=creat(pt[i-1],lower_bound(b+1,b+m+1,a[i])-b,1,m);
//找出当前这个位置按权值排序后的位置x,进入建树
while(k--)in(x),in(y),in(z),printf("%d\n",ask(pt[x-1],pt[y],1,m,z));//注意是【y】-【x-1】
}
七:【可持续化线段树—动态主席树】
\(\text{Dynamic Rankings [P2617]}\) \(\text{[ZOJ2112]}\) \(\text{[BZOJ1901]}\)
#include<algorithm>
#include<cstring>
#include<cstdio>
#define mid (L+R>>1)
#define Re register int
#define F(i,a,b) for(Re i=a;i<=b;++i)
using namespace std;
const int N=2e5+3;char opt[N];
int x,y,z,n,m,T,t,fu,cnt,tl,tr,a[N],b[N],pt[N],C[N],ptl[20],ptr[20];
//ptl,ptr千万不要开N,否则memset的时候会TLE到怀疑人生
struct QAQ{int g,lp,rp;}tree[N*400];//本应是441左右,开小一点也无所谓,因为根本用不到
struct O_O{int l,r,k;}Q[N];//储存Q次查询的内容,方便离散化
struct T_T{int i,x;}c[N];//离散化数组
inline void in(int &x){
x=fu=0;char c=getchar();
while(c<'0'||c>'9')fu|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=fu?-x:x;
}
inline int ask_(Re L,Re R,Re k){
if(L==R)return b[R];//注意:返回的值需要用到的是哪一个映射数组不能搞错
Re tmp=0;
F(i,1,tl)tmp-=tree[tree[ptl[i]].lp].g;//计算左子树信息
F(i,1,tr)tmp+=tree[tree[ptr[i]].lp].g;//计算左子树信息
if(tmp>=k){
F(i,1,tl)ptl[i]=tree[ptl[i]].lp;//更新ptl,ptr所指向的节点编号
F(i,1,tr)ptr[i]=tree[ptr[i]].lp;
return ask_(L,mid,k);
}
else{
F(i,1,tl)ptl[i]=tree[ptl[i]].rp;
F(i,1,tr)ptr[i]=tree[ptr[i]].rp;
return ask_(mid+1,R,k-tmp);
}
}
inline int ask(Re L,Re R,Re k){//查询第k小
memset(ptl,0,sizeof(ptl));//万恶的memset
memset(ptr,0,sizeof(ptr));//数组开太大会疯狂抢时间复杂度
tl=tr=0;
for(Re i=L-1;i;i-=i&-i)ptl[++tl]=pt[i];//先把所有要更新的位置的线段树根节点记录下来
for(Re i=R;i;i-=i&-i)ptr[++tr]=pt[i];//方便后面递归更新信息
return ask_(1,m,k);
}
inline void change(Re &p,Re L,Re R,Re w,Re v){
if(!p)p=++cnt;tree[p].g+=v;
if(L==R)return;
if(w<=mid)change(tree[p].lp,L,mid,w,v);
else change(tree[p].rp,mid+1,R,w,v);
}
inline void add(Re x,Re v){//【单点修改】
Re w=lower_bound(b+1,b+m+1,a[x])-b;//注意函数传进来的参数和这里各种映射数组的调用不要搞错
for(Re i=x;i<=n;i+=i&-i)change(pt[i],1,m,w,v);//树状数组思想更新信息
}
int main(){
// printf("%lf\n",(sizeof(tree))/1024.0/1024.0);
// printf("%lf\n",(sizeof(tree)+sizeof(Q)+sizeof(c)+sizeof(a)+sizeof(b)+sizeof(pt)+sizeof(C))/1024.0/1024.0);
in(n),in(T),m=n;
F(i,1,n)in(a[i]),b[i]=a[i];
F(i,1,T){
scanf(" %c",&opt[i]);
if(opt[i]=='Q')in(Q[i].l),in(Q[i].r),in(Q[i].k);
else in(c[i].i),in(c[i].x),b[++m]=c[i].x;
}
sort(b+1,b+m+1);
m=unique(b+1,b+m+1)-b-1;
F(i,1,n)add(i,1);//初始化建树
F(i,1,T){
if(opt[i]=='Q')printf("%d\n",ask(Q[i].l,Q[i].r,Q[i].k));
else add(c[i].i,-1),a[c[i].i]=c[i].x,add(c[i].i,1);
//先让这个位置上原来的数减少一个,再把新数加一个,就达到了替换的目的
}
}
八:【树套树扩展(树状数组套线段树维护动态平衡树)】
好长的标题啊QAQ。
众所周知,万能的线段树啥都能维护。
#include<algorithm>
#include<cstring>
#include<cstdio>
#define mid (L+R>>1)
#define Re register int
#define pl tree[p].lp
#define pr tree[p].rp
#define F(i,a,b) for(Re i=a;i<=b;++i)
#define lo(o) lower_bound(b+1,b+m+1,o)-b
using namespace std;
const int N=1e5+3,inf=2147483647;//【N不乘2 WA上天】由于要离散化,加上查询最多n+m(即2*n)个数据
int x,y,z,n,m,T,t,fu,cnt,tl,tr,a[N],b[N],pt[N],C[N],opt[N],ptl[20],ptr[20];
struct QAQ{int g,lp,rp;}tree[N*250];//本应是17*17=289左右,开小一点也无所谓,因为根本用不到
struct O_O{int l,r,k;}Q[N];//储存Q次查询的具体内容,方便离散化
struct T_T{int i,x;}c[N];//单点修改的具体内容
inline void in(Re &x){
x=fu=0;char c=getchar();
while(c<'0'||c>'9')fu|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=fu?-x:x;
}
inline int ask_kth(Re L,Re R,Re k){//查询第k小
if(L==R)return b[R];//【映射混用 WA上天】注意:返回的值需要用到的是哪一个映射数组不能搞错
Re tmp=0;
F(i,1,tl)tmp-=tree[tree[ptl[i]].lp].g;//计算左子树信息
F(i,1,tr)tmp+=tree[tree[ptr[i]].lp].g;//计算左子树信息
if(tmp>=k){
F(i,1,tl)ptl[i]=tree[ptl[i]].lp;//更新ptl,ptr所指向的节点编号
F(i,1,tr)ptr[i]=tree[ptr[i]].lp;
return ask_kth(L,mid,k);
}
else{
F(i,1,tl)ptl[i]=tree[ptl[i]].rp;
F(i,1,tr)ptr[i]=tree[ptr[i]].rp;
return ask_kth(mid+1,R,k-tmp);
}
}
inline int ask_kth_pre(Re L,Re R,Re k){//查询第k小(中转站)
tl=tr=0;//(注意L-1)
for(Re i=L-1;i;i-=i&-i)ptl[++tl]=pt[i];//先把所有要更新的位置的线段树根节点记录下来
for(Re i=R;i;i-=i&-i)ptr[++tr]=pt[i];//方便后面递归更新信息
return ask_kth(1,m,k);
}
inline void add(Re &p,Re L,Re R,Re w,Re v){//【单点修改】
if(!p)p=++cnt;tree[p].g+=v;
if(L==R)return;
if(w<=mid)add(pl,L,mid,w,v);
else add(pr,mid+1,R,w,v);
}
inline void add_pre(Re x,Re v){//【单点修改】
Re w=lo(a[x]);//【映射混用 TLE上天】注意函数传进来的参数x是在原数列的位置c[i].i(方便更新原数列),这里各种映射数组的调用不要搞错
for(Re i=x;i<=n;i+=i&-i)add(pt[i],1,m,w,v);//树状数组思想更新信息
}
inline int ask_level(Re p,Re L,Re R,Re x){//查询小于等于x的数的个数
if(L==R)return tree[p].g;
if(x<=mid)return ask_level(pl,L,mid,x);
else return tree[pl].g+ask_level(pr,mid+1,R,x);
}
inline int ask_level_pre(Re L,Re R,Re w){//查询x的排名(中转站)
Re ans=0;
for(Re i=R;i;i-=i&-i)ans+=ask_level(pt[i],1,m,w);
for(Re i=L-1;i;i-=i&-i)ans-=ask_level(pt[i],1,m,w);
return ans;
}
int main(){
// printf("%lf\n",(sizeof(tree))/1024.0/1024.0);
// printf("%lf\n",(sizeof(tree)+sizeof(Q)+sizeof(c)+sizeof(a)+sizeof(b)+sizeof(pt)+sizeof(C))/1024.0/1024.0);
in(n),in(T),m=n;
F(i,1,n)in(a[i]),b[i]=a[i];
F(i,1,T){
in(opt[i]);
if(opt[i]==3)in(c[i].i),in(c[i].x),b[++m]=c[i].x;
else{
in(Q[i].l),in(Q[i].r),in(Q[i].k);
if(opt[i]!=2)b[++m]=Q[i].k;//【不离散 WA上天】除了2的查询不用管,其他地方出现的k全部都要离散化
}
}
sort(b+1,b+m+1);
m=unique(b+1,b+m+1)-b-1;//unique()是-(b+1),lower_bound()是-b
F(i,1,n)add_pre(i,1);//初始化建树
F(i,1,T){
if(opt[i]==1)//查询x的排名(中转站)
Q[i].k=lo(Q[i].k),//【直接查询 WA上天】先查询Q[i].k在b中的的位置,将其减一查得 ≤他前一个数 的总个数
printf("%d\n",ask_level_pre(Q[i].l,Q[i].r,Q[i].k-1)+1);//再加一查得Q[i].k的排名,酱紫可以有效避过Q[i].k的副本处理
if(opt[i]==2)
printf("%d\n",ask_kth_pre(Q[i].l,Q[i].r,Q[i].k));//查询第k小(中转站)
if(opt[i]==3)//修改某一位值上的数值(中转站)
add_pre(c[i].i,-1),a[c[i].i]=c[i].x,add_pre(c[i].i,1);
//先让这个位置上原来的数减少一个,更新数字后再把新数加一个,就达到了替换的目的
if(opt[i]==4){//查询前驱(严格小于)
/*1>取位置*/Q[i].k=lo(Q[i].k);//【直接查询 WA上天】先查询Q[i].k在b中的位置,将其位置减一查询得前驱
/*2>找排名*/Re level=ask_level_pre(Q[i].l,Q[i].r,Q[i].k-1);//因为在离散化数组中是找不到Q[i].k-1这个数字的,所以不能直接查询具体数值
/*3>判有无*/if(!level)printf("%d\n",-inf);//【判断条件错误 WA到上天】由于这里level是取出的前驱在b中的位置,所以只要【level>0】就可以啦
//(如果你按着上面【直接查询 WA上天】的注释改了代码,却没有改这里的【条件判断】,那么你的level<=1将会让你【WA上天】)。
/*4>找结果*/else printf("%d\n",ask_kth_pre(Q[i].l,Q[i].r,level));
}
if(opt[i]==5){//查询猴急(严格大于)【盲目复制 WA上天】如果你采用了同上的方法,等着死翘翘吧
/*1>取位置*/Q[i].k=lo(Q[i].k);
/*2>找排名*/Re level=ask_level_pre(Q[i].l,Q[i].r,Q[i].k);//【直接查询 WA上天】如果同上,会越界,上面的越界是b[0]=0所以不慌,嘿嘿,而这里b[n+1]=0就不行了哟
/*3>判有无*/if(level==Q[i].r-Q[i].l+1)printf("%d\n",inf);//【判断条件错误 WA上天】这里猴急应是level+1,所以条件应是【level≤区间总长度】
/*4>找结果*/else printf("%d\n",ask_kth_pre(Q[i].l,Q[i].r,level+1));//【盲目复制 WA上天】 别忘了加一,和前驱不同啦!
}
}
}