树状数组/线段树/动态开点线段树(板子整理)

树状数组板子

放到结构体里,避免总敲

树状数组求第k大,倍增的思想,类似线段树上二分

Educational Codeforces Round 87 (Rated for Div. 2) D.Multiset为例

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
struct BitPre{ // 求前缀和(可改为max等)
	int n,tr[N];
	void init(int _n){
		n=_n;
		memset(tr,0,(n+1)*sizeof(*tr));
	}
	void addSum(int x,int v){
		for(int i=x;i<=n;i+=i&-i)
		tr[i]+=v;
	}
	void addMx(int x,int v){
		for(int i=x;i<=n;i+=i&-i)
		tr[i]=max(tr[i],v);
	}
	int askSum(int x){
		int ans=0; 
		for(int i=x;i;i-=i&-i)
		ans+=tr[i];
		return ans;
	}
    int askMx(int x){
		int ans=0; 
		for(int i=x;i;i-=i&-i)
		ans=max(ans,tr[i]);
		return ans;
	}
    // 树状数组求从小到大第k个, 1<=k<=sum(n), 1<=x<=n
    int kth(int k){
        int x=0;
        for(int i=1<<std::__lg(n);i;i>>=1){
            if(x+i<=n && k>tr[x+i]){
                x+=i;
                k-=tr[x];
            }
        }
        return x+1;
    }
}tr;
struct BitSuf{ // 求后缀和(可改为max等)
	int n,tr[N];
	void init(int _n){
		n=_n;
		memset(tr,0,(n+1)*sizeof(*tr));
	}
	void add(int x,int v){
		for(int i=x;i;i-=i&-i)
		tr[i]+=v;
	}
	int sum(int x){
		int ans=0; 
		for(int i=x;i<=n;i+=i&-i)
		ans+=tr[i];
		return ans;
	}
}tr2;

二维树状数组

#include<bits/stdc++.h>
using namespace std;
const int N=5e3+10,M=5e3+10;
int n,m;
struct BIT{
	int n,m,tr[N][M];
	void init(int _n,int _m){
		n=_n;m=_m;
		memset(tr,0,sizeof tr);
	}
	void add(int x,int y,int v){
		for(int i=x;i<=n;i+=i&-i){
            for(int j=y;j<=m;j+=j&-j){
                tr[i][j]+=v;
            }
        }
	}
	int sum(int x,int y){
		int ans=0; 
		for(int i=x;i;i-=i&-i){
            for(int j=y;j;j-=j&-j){
                ans+=tr[i][j];
            }
        }
		return ans;
	}
}tr;

线段树板子

板子1(单点赋值,区间求或)

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N];
//单点赋值,区间求或
struct segtree1{
	int n;
	struct node{int l,r,v;}e[N<<2];
	#define l(p) e[p].l
	#define r(p) e[p].r
	#define v(p) e[p].v
	void up(int p){v(p)=v(p<<1)|v(p<<1|1);}
	void bld(int p,int l,int r){
		l(p)=l;r(p)=r;
		if(l==r){v(p)=0;return;}
		int mid=l+r>>1;
		bld(p<<1,l,mid);bld(p<<1|1,mid+1,r);
		up(p);
	}
	void init(int _n){n=_n;bld(1,1,n);}
	void chg(int p,int x,int v){
		if(l(p)==r(p)){v(p)=v;return;}
		int mid=l(p)+r(p)>>1;
		chg(p<<1|(x>mid),x,v);
		up(p);
	}
	int cnt(int p,int ql,int qr){
		if(ql<=l(p)&&r(p)<=qr)return v(p);
		int mid=l(p)+r(p)>>1,res=0;
		if(ql<=mid)res|=cnt(p<<1,ql,qr);
		if(qr>mid)res|=cnt(p<<1|1,ql,qr);
		return res;
	}
}seg1;

板子2(区间加,区间求和)

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N];
//区间加 区间求和
struct segtree2{
	int n;
	struct node{int l,r,v,c;}e[N<<2];
	#define l(p) e[p].l
	#define r(p) e[p].r
	#define v(p) e[p].v
	#define c(p) e[p].c
	void up(int p){v(p)=v(p<<1)+v(p<<1|1);}
	void bld(int p,int l,int r){
		l(p)=l;r(p)=r;
		if(l==r){v(p)=c(p)=0;return;}
		int mid=l+r>>1;
		bld(p<<1,l,mid);bld(p<<1|1,mid+1,r);
		up(p);
	}
	void psd(int p){
		if(c(p)){
			v(p<<1)+=(r(p<<1)-l(p<<1)+1)*c(p);
			c(p<<1)+=c(p);
			v(p<<1|1)+=(r(p<<1|1)-l(p<<1|1)+1)*c(p);		
			c(p<<1|1)+=c(p);
			c(p)=0; 
		}
	}
	void init(int _n){n=_n;bld(1,1,n);}
	void chg(int p,int x,int v){
		if(l(p)==r(p)){v(p)=v;return;}
		int mid=l(p)+r(p)>>1;
		psd(p);
		chg(p<<1|(x>mid),x,v);
		up(p);
	}
	void add(int p,int ql,int qr,int v){
		if(ql<=l(p)&&r(p)<=qr){
			v(p)+=(r(p)-l(p)+1)*v;
			c(p)+=v;
			return;
		}
		psd(p);
		int mid=l(p)+r(p)>>1;
		if(ql<=mid)add(p<<1,ql,qr,v);
		if(qr>mid)add(p<<1|1,ql,qr,v);
		up(p);
	}
	int cnt(int p,int ql,int qr){
		if(ql<=l(p)&&r(p)<=qr)return v(p);
		int mid=l(p)+r(p)>>1,res=0;
		psd(p);
		if(ql<=mid)res+=cnt(p<<1,ql,qr);
		if(qr>mid)res+=cnt(p<<1|1,ql,qr);
		return res;
	}
}seg2;

板子3(区间一次函数复合求值,2021.12.18update)

y=kx+b,

k单点修改,维护区间积;

b区间加值,维护区间和;

sum维护区间左b*右k之和(如区间[1,3],sum=b1*k2*k3+b2*k3+b3)

num维护区间左1*右k之和(如区间[1,3],num=1*k2*k3+1*k3+1)

询问区间一次函数复合的值f_r...f_{l+1}(f_{l}(x))

【模板】区间一次函数复合求值

#include <bits/stdc++.h>
using namespace std;
const int N=5e5+10,mod=998244353;
int n,m,op,x,k,l,r,v,a[N],bb[N],kk[N];
//b,k b区间改,区间和 k单点改,区间积
void add(int &x,int y){
    x=(x+y)%mod;
}
int add2(int x,int y){
    x=(x+y)%mod;
    return x;
}
void mul(int &a,int b){
    a=1ll*a*b%mod;
}
int mul2(int a,int b){
    return 1ll*a*b%mod;
}
struct segtree3{
	int n;
	struct node{
        int l,r,c,b,k,sum,num;
        node(){
            l=r=c=b=k=sum=num=0;
        }
        void show(){
            //printf("[%d,%d] c:%d b:%d k:%d sum:%d num:%d\n",l,r,c,b,k,sum,num);
        }
    }e[N<<2];
	#define l(p) e[p].l
	#define r(p) e[p].r
	#define c(p) e[p].c
	#define b(p) e[p].b
	#define k(p) e[p].k
    #define sum(p) e[p].sum
    #define num(p) e[p].num
	void up(int p){
        b(p)=add2(b(p<<1),b(p<<1|1));k(p)=mul2(k(p<<1),k(p<<1|1));
        sum(p)=add2(mul2(sum(p<<1),k(p<<1|1)),sum(p<<1|1));
        num(p)=add2(mul2(num(p<<1),k(p<<1|1)),num(p<<1|1));
    }
    void show(int p){
        //printf("p:%d [%d,%d] c:%d b:%d k:%d sum:%d num:%d\n",p,l(p),r(p),c(p),b(p),k(p),sum(p),num(p));
    }
	void bld(int p,int l,int r){
		l(p)=l;r(p)=r;
		if(l==r){num(p)=1;k(p)=kk[l];sum(p)=b(p)=bb[l];show(p);return;}
		int mid=l+r>>1;
		bld(p<<1,l,mid);bld(p<<1|1,mid+1,r);
		up(p);
        show(p);
	}
	void psd(int p){
		if(c(p)){
			add(b(p<<1),1ll*(r(p<<1)-l(p<<1)+1)*c(p)%mod);
            add(sum(p<<1),1ll*num(p<<1)*c(p)%mod);
			add(b(p<<1|1),1ll*(r(p<<1|1)-l(p<<1|1)+1)*c(p)%mod);
            add(sum(p<<1|1),1ll*num(p<<1|1)*c(p)%mod);
			add(c(p<<1),c(p));
			add(c(p<<1|1),c(p));
			c(p)=0; 
		}
	}
	void init(int _n){n=_n;bld(1,1,n);}
	void chgk(int p,int x,int v){
		if(l(p)==r(p)){num(p)=1;k(p)=v;show(p);return;}
		int mid=l(p)+r(p)>>1;
		psd(p);
		chgk(p<<1|(x>mid),x,v);
		up(p);
        show(p);
	}
    void addb(int p,int ql,int qr,int v){
		if(ql<=l(p)&&r(p)<=qr){
			add(b(p),1ll*(r(p)-l(p)+1)*v%mod);
            add(sum(p),1ll*num(p)*v%mod);
			add(c(p),v);
			return;
		}
		psd(p);
		int mid=l(p)+r(p)>>1;
		if(ql<=mid)addb(p<<1,ql,qr,v);
		if(qr>mid)addb(p<<1|1,ql,qr,v);
		up(p);
    }
	node ask(int p,int ql,int qr){
		if(ql<=l(p)&&r(p)<=qr){
            e[p].show();
            return e[p];
        }
		int mid=l(p)+r(p)>>1;
		psd(p);
        node ans;
        if(ql>mid){
            ans=ask(p<<1|1,ql,qr);
        }
        else if(qr<=mid){
            ans=ask(p<<1,ql,qr);
        }
        else{
            node x,y;
            x=ask(p<<1,ql,qr);
            y=ask(p<<1|1,ql,qr);
            ans.b=add2(x.b,y.b);ans.k=mul2(x.k,y.k);
            ans.sum=add2(mul2(x.sum,y.k),y.sum);
            ans.num=add2(mul2(x.num,y.k),y.num);
        }    
        ans.show();
        return ans;
    }
}tr;
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i){
        scanf("%d",&kk[i]);
    }
    for(int i=1;i<=n;++i){
        scanf("%d",&bb[i]);
    }
    tr.init(n);
    while(m--){
        scanf("%d",&op);
        if(op==1){
            scanf("%d%d",&x,&k);
            tr.chgk(1,x,k);
        }
        else if(op==2){
            scanf("%d%d%d",&l,&r,&v);
            tr.addb(1,l,r,v);
        }
        else if(op==3){
            scanf("%d%d%d",&l,&r,&x);
            segtree3::node ans=tr.ask(1,l,r);
            int cal=(1ll*ans.k*x%mod+ans.sum)%mod;
            printf("%d\n",cal);
        }
    }
    return 0;
}

板子4(线段树动态增删点,维护区间内当前点的个数,当前区间和、最小值,2023.2.6update)

其实,不如写个动态开点的线段树,这里因为不是动态开点,

所以,多开的位置的cnt为0,区间和sum为0,最小值mn为INF

注意,删点时也要置cnt为0,区间和sum为0,最小值mn为INF

代码以此题为例:

Codeforces Round #850 (Div. 1, based on VK Cup 2022 - Final Round) C. Monsters (hard version)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int N=2e5+10,M=2e5,INF=0x3f3f3f3f;
int t,n,a[N],rk[N],now[N],pos;
P b[N];
struct segtree{
	int n;
	struct node{int l,r,c,num;ll mn,v;}e[N<<2];
	#define l(p) e[p].l
	#define r(p) e[p].r
	#define v(p) e[p].v
	#define c(p) e[p].c
    #define n(p) e[p].num
    #define m(p) e[p].mn
	void up(int p){
        v(p)=v(p<<1)+v(p<<1|1);
        n(p)=n(p<<1)+n(p<<1|1);
        m(p)=min(m(p<<1),m(p<<1|1));
    }
	void psd(int p){
		if(c(p)){
            if(n(p<<1)){
                v(p<<1)+=1ll*n(p<<1)*c(p);
                m(p<<1)+=c(p);
                c(p<<1)+=c(p);
            }
            if(n(p<<1|1)){
                v(p<<1|1)+=1ll*n(p<<1|1)*c(p);
                m(p<<1|1)+=c(p);		
                c(p<<1|1)+=c(p);
            }
			c(p)=0;
		}
	}
    void bld(int p,int l,int r){
		l(p)=l;r(p)=r;
		if(l==r){v(p)=c(p)=n(p)=0;m(p)=INF;return;}
		int mid=l+r>>1;
		bld(p<<1,l,mid);bld(p<<1|1,mid+1,r);
		up(p);
    }
	void init(int _n){n=_n;bld(1,1,n);}
	void add(int p,int x,ll v){ // 动态加点,赋值
		if(l(p)==r(p)){v(p)=v;m(p)=v;n(p)=1;return;}
		int mid=l(p)+r(p)>>1;
		psd(p);
		add(p<<1|(x>mid),x,v);
		up(p);
	}
    void del(int p,int x){ // 动态删点
		if(l(p)==r(p)){v(p)=0;m(p)=INF;n(p)=0;return;}
		int mid=l(p)+r(p)>>1;
		psd(p);
		del(p<<1|(x>mid),x);
		up(p);
	}
	void radd(int p,int ql,int qr,ll v){ // 对已经存在的点区间加值
        if(ql>qr)return;
		if(ql<=l(p)&&r(p)<=qr){
            if(n(p)){
                v(p)+=1ll*n(p)*v;
                m(p)+=v;
                c(p)+=v;
            }
            return;
		}
		psd(p);
		int mid=l(p)+r(p)>>1;
		if(ql<=mid)radd(p<<1,ql,qr,v);
		if(qr>mid)radd(p<<1|1,ql,qr,v);
		up(p);
	}
	ll qsum(int p,int ql,int qr){ // 求sum
        if(ql>qr)return 0;
		if(ql<=l(p)&&r(p)<=qr)return v(p);
		int mid=l(p)+r(p)>>1;
        ll res=0;
		psd(p);
		if(ql<=mid)res+=qsum(p<<1,ql,qr);
		if(qr>mid)res+=qsum(p<<1|1,ql,qr);
		return res;
	}
	ll qmn(int p,int ql,int qr){ // 求min
        if(ql>qr)return 0;
		if(ql<=l(p)&&r(p)<=qr)return m(p);
		int mid=l(p)+r(p)>>1;
        ll res=0;
		psd(p);
		if(ql<=mid)res=min(res,qmn(p<<1,ql,qr));
		if(qr>mid)res=min(res,qmn(p<<1|1,ql,qr));
		return res;
	}
    int fmn(int p){ // 找最左第一个<0的位置
        if(m(p)>=0)return -1;
        if(l(p)==r(p))return l(p);
        int mid=l(p)+r(p)>>1;
        psd(p);
        if(m(p<<1)<0)return fmn(p<<1);
        return fmn(p<<1|1);
    }
}tr1,tr2;
int main(){
    scanf("%d",&t);
    tr1.init(M);
    tr2.init(M);
    while(t--){
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%d",&a[i]);
            b[i]=P(a[i],i);
        }
        sort(b+1,b+n+1);
        for(int i=1;i<=n;++i){
            rk[b[i].second]=i;
        }
        for(int i=1;i<=n;++i){
            ll pre=tr1.qsum(1,1,rk[i])+1;
            tr1.add(1,rk[i],1); //add
            tr2.add(1,rk[i],a[i]-pre); //add
            now[rk[i]]++;
            tr2.radd(1,rk[i]+1,M,-1); // -1
            while((pos=tr2.fmn(1))>=0){
                now[pos]--;
                tr1.del(1,pos); // del
                tr2.del(1,pos); // del
                tr2.radd(1,pos+1,M,1); // +1
            }
            printf("%lld%c",tr2.qsum(1,1,M)," \n"[i==n]);
        }
        for(int i=1;i<=n;++i){
            if(now[rk[i]]){
                now[rk[i]]--;
                tr1.del(1,rk[i]); // del
                tr2.del(1,rk[i]); // del
            }
        }
    }
	return 0;
}

动态开点线段树

和主席树的区别,在于if(!cur)的判断,

即根如果共用,即为动态开点线段树,

如果不共用,每次新开一个根,即为主席树

void newNode(int &cur){
        if(!cur){
            cur=++cnt;
            e[cnt].init();
        }
    }

板子1(单点更新,区间询问,最大值)

#include<bits/stdc++.h>
using namespace std;
struct DynamicSegmentTree{
	static const int N=3e5+10;
	int cnt;
	struct node{
		int l,r,mx;
		void init(){l=r=mx=0;}
		node(){init();} 
	}e[N*40];
	void init(){
		e[0].init();
		cnt=0;
	}
	DynamicSegmentTree(){init();}
	//1e5开*40,1e6开*45
	//其实开(log(maxn)*(n+m)*maxn)就好
	void newNode(int &cur){
		if(!cur){
			cur=++cnt;
			e[cnt].init();
		}
	}
	void upd(int l,int r,int &cur,int pos,int v){
		newNode(cur);
		e[cur].mx=max(e[cur].mx,v);
		if(l==r)return;
		int mid=(l+r)/2;
		if(pos<=mid)upd(l,mid,e[cur].l,pos,v);
		else upd(mid+1,r,e[cur].r,pos,v); 
	}
	int ask(int l,int r,int cur,int ql,int qr){
		if(!cur)return 0;
		if(ql<=l&&r<=qr)return e[cur].mx;
		int ans=0,mid=(l+r)/2;
		if(ql<=mid)ans=max(ans,ask(l,mid,e[cur].l,ql,qr));
		if(qr>mid)ans=max(ans,ask(mid+1,r,e[cur].r,ql,qr));
		return ans;
	}
}tr2;

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值