树状数组板子
放到结构体里,避免总敲
树状数组求第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)
询问区间一次函数复合的值
#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;