线段树科技合订本
线段树和矩阵
矩阵不满足交换律,但满足结合律,所以我们可以用线段树维护矩阵乘法和广义矩阵乘法,甚至还能上树剖,这就是 ddp,可以去搜 “动态 dp”,对矩阵进行修改可以更改转移,不难理解。
线段树历史版本和
操作一个序列:
- \(1.\) 将 \([l,r]\) 加上 \(x\)
- \(2.\) 区间 \([l,r]\) 生成一次历史版本
- \(3.\) 查询区间 \([l,r]\) 的历史版本和
维护信息如下:
\(\texttt{res}\):区间历史和
\(\texttt{sum}\):区间和
维护懒标记 \(\texttt{tag}\) 如下:
\(\texttt{tag}\):常规区间加标记(清空)
\(\texttt{tagh}\):历史区间加总和标记(清空)
\(\texttt{cnt}\):区间加次数(清空)
线段树最重要在 \(\texttt{Pushdown}\),也就是标记的合并,怎么办?
假设现在有两个标记队列 \(q_1,q_2\),标记有加上 \(x\) 或者生成 \(x\) 次历史版本。
考虑每个标记对单个数的贡献,贡献为 \(\texttt{(标记的大小)} \times \texttt(标记以后保存历史版本次数)\),贡献的东西就存在 \(\texttt{tagh}\) 里面。
那么简单了,两个标记合并我们考虑跨过标记的贡献,也就是先来的的标记大小和和乘上后来的的历史版本次数。我们认为标记内部的已经贡献完毕了。
那么,最重要的下方标记已经清楚了,代码如下:
#include<bits/stdc++.h>
#define N 100005
#define ll long long
using namespace std;
int n,m;
struct tags{
ll htag,tag,cnt;
};
struct segtree{
tags w[N*4];
ll sum[N*4],res[N*4];
void Pushup(int x)
{
sum[x]=sum[x*2]+sum[x*2+1];
res[x]=res[x*2]+res[x*2+1];
}
void updata(int x,tags v,ll l)
{
res[x]+=sum[x]*v.cnt+v.htag*l;
sum[x]+=l*v.tag;
w[x].htag+=v.htag+w[x].tag*v.cnt;
w[x].cnt+=v.cnt;
w[x].tag+=v.tag;
}
void Pushdown(int x,ll l)
{
updata(x*2,w[x],l-(l/2));
updata(x*2+1,w[x],l/2);
w[x]={0};
}
void modify(int l,int r,int L,int R,int x,ll w)
{
if(l>R||r<L) return;
if(l>=L&&r<=R)
{
updata(x,(tags){0,w,0},r-l+1);
return;
}
int mid=(l+r)/2;
Pushdown(x,r-l+1);
modify(l,mid,L,R,x*2,w);
modify(mid+1,r,L,R,x*2+1,w);
Pushup(x);
}
ll query(int l,int r,int L,int R,int x)
{
if(l>R||r<L) return 0;
if(l>=L&&r<=R) return res[x];
int mid=(l+r)/2;
Pushdown(x,r-l+1);
return query(l,mid,L,R,x*2)+query(mid+1,r,L,R,x*2+1);
}
void save()
{
updata(1,{0,0,1},n);
}
}tr;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
tr.modify(1,n,i,i,1,x);
}
while(m--)
{
tr.save();
int opr,l,r,w;
scanf("%d%d%d",&opr,&l,&r);
if(opr==1) scanf("%d",&w),tr.modify(1,n,l,r,1,w);
else printf("%lld\n",tr.query(1,n,l,r,1));
}
return 0;
}
线段树区间翻转/插入/删除
需要广义线段树。不能可持久化。
将操作区间分成三份,分别是 \([1,l-1]\),\([l,r]\),\([l+1,r]\),暴力对中间的树进行操作,操作完对几棵子树建一棵完美线段树,均摊时间复杂度是能在 \(O(\log n)\) 复杂度完成的。节点数恒为 \(2n-1\),需要细节的垃圾节点回收。
平衡树能干的它都能做,除了 LCT。
如果整棵树空了会出现特别严重的错误,包括但不限于 RE,TLE,MLE,WA。
这里给出 [NOI2005] 维护数列 的 code。
#include<bits/stdc++.h>
#define N 500005
#define ll long long
#define ls(x) tr[x].l
#define rs(x) tr[x].r
using namespace std;
int n,m,T;
const int inf=1e9;
struct vals{
int sum,lmax,rmax,maxx;
void fill(int x,int l)
{
sum=x*l;
maxx=lmax=rmax=max(x*l,x);
}
void rev()
{
swap(lmax,rmax);
}
};
struct tnode{
int l,r,siz;
vals w;
int rev,cov;
};
vals operator *(vals a,vals b)
{
return (vals){
a.sum+b.sum,
max(a.lmax,b.lmax+a.sum),
max(b.rmax,a.rmax+b.sum),
max(a.maxx,max(b.maxx,a.rmax+b.lmax))
};
}
int rt;
struct segtree{
tnode tr[N*2];
int buc[N*2],top,tot;
inline int gnode()
{
int id=top?buc[top--]:++tot;
tr[id]={0};tr[id].cov=inf;
return id;
}
void Pushup(int x)
{
tr[x].w=tr[ls(x)].w*tr[rs(x)].w;
tr[x].siz=tr[ls(x)].siz+tr[rs(x)].siz;
}
void Pushdown(int x)
{
if(tr[x].rev)
{
tr[ls(x)].rev^=1;
tr[rs(x)].rev^=1;
tr[ls(x)].w.rev();
tr[rs(x)].w.rev();
swap(ls(ls(x)),rs(ls(x)));
swap(ls(rs(x)),rs(rs(x)));
tr[x].rev=0;
}
if(tr[x].cov!=inf)
{
tr[ls(x)].w.fill(tr[x].cov,tr[ls(x)].siz);
tr[rs(x)].w.fill(tr[x].cov,tr[rs(x)].siz);
tr[ls(x)].cov=tr[rs(x)].cov=tr[x].cov;
tr[x].cov=inf;
}
}
int merges(int l,int r)
{
int x=gnode();
ls(x)=l,rs(x)=r;
Pushup(x);
return x;
}
int bl[N],bm[N],br[N],dl,dm,dr;
void split(int u,int l,int r,int L,int R)
{
if(r<L){bl[++dl]=u;return;}
if(l>R){br[++dr]=u;return;}
if(l>=L&&r<=R){bm[++dm]=u;return;}
int mid=l-1+tr[ls(u)].siz;
Pushdown(u);
split(ls(u),l,mid,L,R);
split(rs(u),mid+1,r,L,R);
buc[++top]=u;
}
int w,p[N];
void mergeall()
{
while(w>1)
{
for(int i=2;i<=w;i+=2)
p[i/2]=merges(p[i-1],p[i]);
if(w&1) p[(w+1)/2]=p[w];
w=(w+1)/2;
}
rt=p[1];
}
void dfs(int now)
{
if(!now) return;
buc[++top]=now;
dfs(ls(now)),dfs(rs(now));
}
void del(int l,int r)
{
dl=dm=dr=w=0;
split(rt,1,n,l,r);
for(int i=1;i<=dl;i++) p[++w]=bl[i];
for(int i=1;i<=dm;i++) dfs(bm[i]);
for(int i=1;i<=dr;i++) p[++w]=br[i];
mergeall();
n-=r-l+1;
}
void reverse(int l,int r)
{
dl=dm=dr=w=0;
split(rt,1,n,l,r);
for(int i=1;i<=dl;i++) p[++w]=bl[i];
for(int i=dm;i>=1;i--)
{
p[++w]=bm[i];
swap(ls(bm[i]),rs(bm[i]));
tr[bm[i]].rev^=1;
tr[bm[i]].w.rev();
}
for(int i=1;i<=dr;i++) p[++w]=br[i];
mergeall();
}
void ins(int *a,int m,int pos)
{
dl=dm=dr=w=0;
for(int i=1;i<=m;i++) scanf("%d",&a[i]);
split(rt,1,n,pos,pos);
for(int i=1;i<=dl;i++) p[++w]=bl[i];
for(int i=1;i<=dm;i++) p[++w]=bm[i];
for(int i=1;i<=m;i++)
{
int x=gnode();
tr[x].w.fill(a[i],1);
tr[x].siz=1;
p[++w]=x;
}
for(int i=1;i<=dr;i++) p[++w]=br[i];
mergeall();
n+=m;
}
void modify(int u,int l,int r,int L,int R,int v)
{
if(l>R||r<L) return;
if(l>=L&&r<=R)
{
tr[u].cov=v;
tr[u].w.fill(v,r-l+1);
return;
}
int mid=l+tr[ls(u)].siz-1;
Pushdown(u);
modify(ls(u),l,mid,L,R,v);
modify(rs(u),mid+1,r,L,R,v);
Pushup(u);
}
int query(int u,int l,int r,int L,int R)
{
if(l>R||r<L) return 0;
if(l>=L&&r<=R) return tr[u].w.sum;
int mid=l+tr[ls(u)].siz-1;
Pushdown(u);
return query(ls(u),l,mid,L,R)+query(rs(u),mid+1,r,L,R);
}
}tr;
int a[N];
char opr[12];
int main()
{
scanf("%d%d",&m,&T);
tr.ins(a,m,0);
while(T--)
{
int tot,l,r,w;
scanf("%s",opr+1);
if(opr[3]^'X') scanf("%d%d",&l,&tot),r=l+tot-1;
if(opr[1]=='I') tr.ins(a,tot,l);
if(opr[1]=='D') tr.del(l,r);
if(opr[3]=='K') scanf("%d",&w),tr.modify(rt,1,n,l,r,w);
if(opr[1]=='R') tr.reverse(l,r);
if(opr[1]=='G') printf("%d\n",tr.query(rt,1,n,l,r));
if(opr[3]=='X') printf("%d\n",tr.tr[rt].w.maxx);
}
return 0;
}
其实叫 无旋自适应Leafy tree
segment beat
咕咕咕