线段树历史值
P6242 【模板】线段树 3
支持区间加,区间取 \(\min\),区间求和,区间 \(\max\),区间历史 \(\max\)。
先提一嘴吉司机。
就是对线段树的每个节点记录最大值,严格次大值和最大值个数,只在 \(se<v<mx\) 的区间操作,否则向下递归。如果没有区间加,复杂度势能分析是 \(O((n+m)\log n)\) 的,否则为 \(O(m\log^2 n)\)。
考虑 \(\operatorname{pushdown}\) 需要的 \(\mathrm{tag}\)。
-
\(\mathrm{tag1}\):区间 \(\max\) 的懒标记。
-
\(\mathrm{tag2}\):区间非 \(\max\) 的懒标记。
-
\(\mathrm{tag3}\):区间 \(\max\) 的懒标记的最大值。
-
\(\mathrm{tag4}\):区间非 \(\max\) 的懒标记的最大值。
注意要先更新历史值再更新当前值。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define N 500010
#define inf 2e9
using namespace std;
int read(){
int x=0,w=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*w;
}
#define sum(x) tr[x].sum
#define len(x) tr[x].len
#define ma(x) tr[x].ma
#define cnt(x) tr[x].cnt
#define se(x) tr[x].se
#define mb(x) tr[x].mb
#define add1(x) tr[x].add1
#define add2(x) tr[x].add2
#define add3(x) tr[x].add3
#define add4(x) tr[x].add4
struct Tree{
ll sum;int len;
int ma,cnt,se,mb;
int add1,add2,add3,add4;
}tr[N<<2];
#define ls p<<1
#define rs p<<1|1
void pushup(int p){
sum(p)=sum(ls)+sum(rs);
ma(p)=max(ma(ls),ma(rs)),mb(p)=max(mb(ls),mb(rs));
if(ma(ls)==ma(rs))
se(p)=max(se(ls),se(rs)),cnt(p)=cnt(ls)+cnt(rs);
else if(ma(ls)>ma(rs))
se(p)=max(se(ls),ma(rs)),cnt(p)=cnt(ls);
else
se(p)=max(ma(ls),se(rs)),cnt(p)=cnt(rs);
}
void change(int p,int k1,int k2,int k3,int k4){
sum(p)+=1ll*k1*cnt(p)+1ll*k2*(len(p)-cnt(p));
mb(p)=max(mb(p),ma(p)+k3),ma(p)+=k1;
if(se(p)!=-inf)se(p)+=k2;
add3(p)=max(add3(p),add1(p)+k3);
add4(p)=max(add4(p),add2(p)+k4);
add1(p)+=k1,add2(p)+=k2;
}
void pushdown(int p){
int mx=max(ma(ls),ma(rs));
if(ma(ls)==mx)
change(ls,add1(p),add2(p),add3(p),add4(p));
else change(ls,add2(p),add2(p),add4(p),add4(p));
if(ma(rs)==mx)
change(rs,add1(p),add2(p),add3(p),add4(p));
else change(rs,add2(p),add2(p),add4(p),add4(p));
add1(p)=add2(p)=add3(p)=add4(p)=0;
}
void build(int p,int l,int r){
len(p)=r-l+1;
if(l==r){
sum(p)=ma(p)=mb(p)=read();
cnt(p)=1,se(p)=-inf;
return;
}
int mid=(l+r)>>1;
build(ls,l,mid),build(rs,mid+1,r);
pushup(p);
}
ll qsum(int p,int l,int r,int L,int R){
if(L<=l&&r<=R)return sum(p);
int mid=(l+r)>>1;pushdown(p);
ll ret=0;
if(L<=mid)ret+=qsum(ls,l,mid,L,R);
if(R>mid)ret+=qsum(rs,mid+1,r,L,R);
return ret;
}
int qma(int p,int l,int r,int L,int R){
if(L<=l&&r<=R)return ma(p);
int mid=(l+r)>>1;pushdown(p);
if(R<=mid)return qma(ls,l,mid,L,R);
if(L>mid)return qma(rs,mid+1,r,L,R);
return max(qma(ls,l,mid,L,R),qma(rs,mid+1,r,L,R));
}
int qmb(int p,int l,int r,int L,int R){
if(L<=l&&r<=R)return mb(p);
int mid=(l+r)>>1;pushdown(p);
if(R<=mid)return qmb(ls,l,mid,L,R);
if(L>mid)return qmb(rs,mid+1,r,L,R);
return max(qmb(ls,l,mid,L,R),qmb(rs,mid+1,r,L,R));
}
void uadd(int p,int l,int r,int L,int R,int k){
if(L<=l&&r<=R){
sum(p)+=1ll*k*len(p);
ma(p)+=k,mb(p)=max(mb(p),ma(p));
if(se(p)!=-inf)se(p)+=k;
add1(p)+=k,add2(p)+=k;
add3(p)=max(add3(p),add1(p)),add4(p)=max(add4(p),add2(p));
return;
}
int mid=(l+r)>>1;pushdown(p);
if(L<=mid)uadd(ls,l,mid,L,R,k);
if(R>mid)uadd(rs,mid+1,r,L,R,k);
pushup(p);
}
void umin(int p,int l,int r,int L,int R,int v){
if(L>r||R<l||v>=ma(p))return;
if(L<=l&&r<=R&&se(p)<v){
int k=ma(p)-v;
sum(p)-=1ll*cnt(p)*k;
ma(p)=v,add1(p)-=k;
return;
}
int mid=(l+r)>>1;pushdown(p);
umin(ls,l,mid,L,R,v);
umin(rs,mid+1,r,L,R,v);
pushup(p);
}
int n,m;
int main(){
n=read(),m=read();
build(1,1,n);
for(int opt,l,r;m;m--){
opt=read(),l=read(),r=read();
if(opt==1)uadd(1,1,n,l,r,read());
if(opt==2)umin(1,1,n,l,r,read());
if(opt==3)printf("%lld\n",qsum(1,1,n,l,r));
if(opt==4)printf("%d\n",qma(1,1,n,l,r));
if(opt==5)printf("%d\n",qmb(1,1,n,l,r));
}
return 0;
}
P4314 CPU 监控
支持区间 \(\max\),区间历史 \(\max\),区间加,区间推平。
记录 \(\mathrm{tag1}\) 和 \(\mathrm{tag2}\) 表示区间加和推平的懒标记。
为了维护历史 \(\max\),新增两个懒标记 \(\mathrm{mtag1}\) 和 \(\mathrm{mtag2}\)。
点击查看代码
#include<bits/stdc++.h>
#define N 100010
using namespace std;
int read(){
int x=0,w=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*w;
}
char gets(){
char ch=getchar();
while(ch<'A'||ch>'Z')ch=getchar();
return ch;
}
#define mx(x) tr[x].mx
#define mxx(x) tr[x].mxx
#define tag1(x) tr[x].tag1
#define tag2(x) tr[x].tag2
#define mtag1(x) tr[x].mtag1
#define mtag2(x) tr[x].mtag2
#define assign(x) tr[x].assign
struct Tree{
int mx,mxx;
int tag1,tag2,mtag1,mtag2;
bool assign;
}tr[N<<2];
#define ls p<<1
#define rs p<<1|1
void pushup(int p){
mx(p)=max(mx(ls),mx(rs));
mxx(p)=max(mxx(ls),mxx(rs));
}
void utag1(int p,int tag,int mtag){
mxx(p)=max(mxx(p),mx(p)+mtag),mx(p)+=tag;
if(assign(p)){
mtag2(p)=max(mtag2(p),tag2(p)+mtag);
tag2(p)+=tag;
}
else{
mtag1(p)=max(mtag1(p),tag1(p)+mtag);
tag1(p)+=tag;
}
}
void utag2(int p,int tag,int mtag){
tag2(p)=mx(p)=tag,mxx(p)=max(mxx(p),mtag);
if(assign(p))
mtag2(p)=max(mtag2(p),mtag);
else assign(p)=true,mtag2(p)=mtag;
}
void pushdown(int p){
utag1(ls,tag1(p),mtag1(p));
utag1(rs,tag1(p),mtag1(p));
tag1(p)=mtag1(p)=0;
if(assign(p)){
utag2(ls,tag2(p),mtag2(p));
utag2(rs,tag2(p),mtag2(p));
assign(p)=tag2(p)=mtag2(p)=0;
}
}
void build(int p,int l,int r){
if(l==r)return mx(p)=mxx(p)=read(),void();
int mid=(l+r)>>1;
build(ls,l,mid),build(rs,mid+1,r);
pushup(p);
}
void uadd(int p,int l,int r,int L,int R,int x){
if(L<=l&&r<=R)return utag1(p,x,x);
int mid=(l+r)>>1;pushdown(p);
if(L<=mid)uadd(ls,l,mid,L,R,x);
if(R>mid)uadd(rs,mid+1,r,L,R,x);
pushup(p);
}
void uval(int p,int l,int r,int L,int R,int x){
if(L<=l&&r<=R)return utag2(p,x,x);
int mid=(l+r)>>1;pushdown(p);
if(L<=mid)uval(ls,l,mid,L,R,x);
if(R>mid)uval(rs,mid+1,r,L,R,x);
pushup(p);
}
int qcur(int p,int l,int r,int L,int R){
if(L<=l&&r<=R)return mx(p);
int mid=(l+r)>>1;pushdown(p);
if(R<=mid)return qcur(ls,l,mid,L,R);
if(L>mid)return qcur(rs,mid+1,r,L,R);
return max(qcur(ls,l,mid,L,R),qcur(rs,mid+1,r,L,R));
}
int qall(int p,int l,int r,int L,int R){
if(L<=l&&r<=R)return mxx(p);
int mid=(l+r)>>1;pushdown(p);
if(R<=mid)return qall(ls,l,mid,L,R);
if(L>mid)return qall(rs,mid+1,r,L,R);
return max(qall(ls,l,mid,L,R),qall(rs,mid+1,r,L,R));
}
int n,m;
int main(){
n=read(),build(1,1,n);
m=read();
char opt;int l,r;
while(m--){
opt=gets(),l=read(),r=read();
if(opt=='Q')printf("%d\n",qcur(1,1,n,l,r));
if(opt=='A')printf("%d\n",qall(1,1,n,l,r));
if(opt=='P')uadd(1,1,n,l,r,read());
if(opt=='C')uval(1,1,n,l,r,read());
}
return 0;
}
GSS2 - Can you answer these queries II
询问区间最大子段和。一种数只计算一次。
将询问离线后按右端点排序。如果固定答案右端点为当前的 \(r\),可以用线段树直接做。具体地,每个叶子节点维护 \(\sum\limits_{k\in[pos,i]}a_k\) 即可。
但是右端点为 \(r\) 不一定最优,所以再维护历史 \(\max\).
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define N 100010
using namespace std;
int read(){
int x=0,w=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*w;
}
#define m1(x) tr[x].m1
#define m2(x) tr[x].m2
#define tag1(x) tr[x].tag1
#define tag2(x) tr[x].tag2
struct Tree{
ll m1,m2,tag1,tag2;
}tr[N<<2];
#define ls p<<1
#define rs p<<1|1
void pushup(int p){
m1(p)=max(m1(ls),m1(rs));
m2(p)=max(m2(ls),m2(rs));
}
void pushdown(int p){
m2(ls)=max(m2(ls),m1(ls)+tag2(p));
m2(rs)=max(m2(rs),m1(rs)+tag2(p));
tag2(ls)=max(tag2(ls),tag1(ls)+tag2(p));
tag2(rs)=max(tag2(rs),tag1(rs)+tag2(p));
m1(ls)+=tag1(p),m1(rs)+=tag1(p);
tag1(ls)+=tag1(p),tag1(rs)+=tag1(p);
tag1(p)=tag2(p)=0;
}
void modify(int p,int l,int r,int L,int R,int v){
if(L<=l&&r<=R){
m1(p)+=v,tag1(p)+=v;
m2(p)=max(m2(p),m1(p)),tag2(p)=max(tag2(p),tag1(p));
return;
}
int mid=(l+r)>>1;pushdown(p);
if(L<=mid)modify(ls,l,mid,L,R,v);
if(R>mid)modify(rs,mid+1,r,L,R,v);
pushup(p);
}
ll query(int p,int l,int r,int L,int R){
if(L<=l&&r<=R)return m2(p);
int mid=(l+r)>>1;pushdown(p);
if(R<=mid)return query(ls,l,mid,L,R);
if(L>mid)return query(rs,mid+1,r,L,R);
return max(query(ls,l,mid,L,R),query(rs,mid+1,r,L,R));
}
struct Q{
int l,r,id;
bool operator<(const Q &t)const{
return r<t.r;
}
}q[N];
int n,m,a[N];
int dlt=100000,lst[N<<1],pre[N];
ll ans[N];
int main(){
n=read();
for(int i=1;i<=n;i++){
a[i]=read();
pre[i]=lst[a[i]+dlt],lst[a[i]+dlt]=i;
}
m=read();
for(int i=1,l,r;i<=m;i++){
l=read(),r=read();
q[i]=(Q){l,r,i};
}
sort(q+1,q+1+m);
for(int i=1,j=1;i<=n;i++){
modify(1,1,n,pre[i]+1,i,a[i]);
while(j<=m&&q[j].r==i)
ans[q[j].id]=query(1,1,n,q[j].l,i),j++;
}
for(int i=1;i<=m;i++)
printf("%lld\n",ans[i]);
return 0;
}
P8868 [NOIP2022] 比赛
多次询问 \(\displaystyle \sum_{p=l}^{r}\sum_{q=p}^{r}(\max_{i\in[p,q]}\{a_i\})\cdot (\max_{j\in[p,q]}\{b_j\})\)。
\(\{a_n\}\),\(\{b_n\}\) 均为 \(1\) 到 \(n\) 的排列,答案对 \(2^{64}\) 取模。
\(n\le 2.5\times 10^5\)。
扫描线之后,容易单调栈出线段树上的操作区间。想想现在要干嘛:
-
对 \(a\) 区间加。
-
对 \(b\) 区间加。
-
对 \(s_i\) 加上 \(a_i\cdot b_i\)。
-
查询 \(s_i\) 区间和。
维护点积属实是有点逆天了。先想想正常的历史和。
区间加历史和
线段树节点上维护了区间和 \(sum\),区间历史和 \(ans\),加法标记 \(add\)。
-
\(+x\) 标记:\(sum\leftarrow sum+x\cdot len\),\(add\leftarrow add+x\)。
-
\(\mathrm{update}\) 标记:\(ans\leftarrow ans+sum\)。
考虑 \(\mathrm{pushdown}\),记儿子为 \(1\),父亲为 \(2\)。
-
\(add_1\) 和 \(sum_1\) 是容易的。
-
\(ans_1\leftarrow ans_1+sum_1\cdot upd_2+h_2\cdot len_1\),其中 \(upd\) 为 \(\mathrm{update}\) 操作的个数,\(h\) 为所有 \(\mathrm{update}\) 操作的 \(add\) 之和。
还要维护 \(upd\) 和 \(h\),\(upd\) 是容易的,\(h_1\leftarrow h_1+add_1\cdot upd_2+h_2\)。
区间加区间点积历史和
回到原题。线段树上应维护区间 \(ab\) 的和 \(s\),区间 \(a\) 的和 \(sa\),区间 \(b\) 的和 \(sb\),\(ab\) 的历史和 \(ans\),加 \(a\) 标记 \(adda\),加 \(b\) 标记 \(addb\)。
-
\(a+x\) 标记:\(s\leftarrow s+x\cdot sb\),\(sa\leftarrow sa+x\cdot len\),\(adda\leftarrow adda+x\)。
-
\(b+x\) 标记:\(s\leftarrow s+x\cdot sa\),\(sb\leftarrow sb+x\cdot len\),\(addb\leftarrow addb+x\)。
-
\(\mathrm{update}\) 标记:\(ans\leftarrow ans+s\)。
考虑 \(\mathrm{pushdown}\),记儿子为 \(1\),父亲为 \(2\)。
除了 \(ans\) 都是容易的。
- \(ans_1\leftarrow ans_1+s_1\cdot upd_2+hb_2\cdot sa_1+ha_2\cdot sb_1+h_2\cdot len_1\),其中 \(upd\) 表示 \(\mathrm{update}\) 操作的个数,\(ha\) 为 \(adda\) 之和,\(hb\) 为 \(addb\) 之和,\(h\) 为 \(adda\cdot addb\) 之和。
还需要维护 \(upd,ha,hb,h\),其中 \(upd\) 是容易的。
-
\(ha_1\leftarrow ha_1+adda_1\cdot upd_2+ha_2\),\(hb\) 同理。
-
\(h_1\leftarrow h_1+adda_1\cdot addb_1\cdot upd_2+adda_1\cdot hb_2+addb_1\cdot ha_2+h_2\)。
维护上述所有标记。
点击查看代码
#include<bits/stdc++.h>
#define int unsigned long long
#define ll unsigned long long
#define N 250010
using namespace std;
int read(){
int x=0,w=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*w;
}
#define adda(x) tr[x].adda
#define addb(x) tr[x].addb
#define s(x) tr[x].s
#define sa(x) tr[x].sa
#define sb(x) tr[x].sb
#define upd(x) tr[x].upd
#define h(x) tr[x].h
#define ha(x) tr[x].ha
#define hb(x) tr[x].hb
#define ans(x) tr[x].ans
#define len(x) tr[x].len
struct Tree{
ll adda,addb,s,sa,sb;
ll upd,h,ha,hb,ans,len;
}tr[N<<2];
#define ls p<<1
#define rs p<<1|1
void pushup(int p){
s(p)=s(ls)+s(rs);
sa(p)=sa(ls)+sa(rs),sb(p)=sb(ls)+sb(rs);
ans(p)=ans(ls)+ans(rs);
}
void spread(int p,Tree v){
ans(p)+=s(p)*v.upd+sa(p)*v.hb+sb(p)*v.ha+v.h*len(p);
h(p)+=adda(p)*addb(p)*v.upd+adda(p)*v.hb+addb(p)*v.ha+v.h;
ha(p)+=adda(p)*v.upd+v.ha;
hb(p)+=addb(p)*v.upd+v.hb;
s(p)+=sa(p)*v.addb+sb(p)*v.adda+v.adda*v.addb*len(p);
sa(p)+=v.adda*len(p),sb(p)+=v.addb*len(p);
upd(p)+=v.upd,adda(p)+=v.adda,addb(p)+=v.addb;
}
void pushdown(int p){
spread(ls,tr[p]),spread(rs,tr[p]);
h(p)=ha(p)=hb(p)=upd(p)=adda(p)=addb(p)=0;
}
void build(int p,int l,int r){
len(p)=r-l+1;
if(l==r)return;
int mid=(l+r)>>1;
build(ls,l,mid),build(rs,mid+1,r);
}
void modify(int p,int l,int r,int L,int R,ll x,int o){
if(L<=l&&r<=R){
if(!o)spread(p,(Tree){x,0,0,0,0,0,0,0,0,0,len(p)});
else spread(p,(Tree){0,x,0,0,0,0,0,0,0,0,len(p)});
return;
}
int mid=(l+r)>>1;pushdown(p);
if(L<=mid)modify(ls,l,mid,L,R,x,o);
if(R>mid)modify(rs,mid+1,r,L,R,x,o);
pushup(p);
}
ll query(int p,int l,int r,int L,int R){
if(L<=l&&r<=R)return ans(p);
int mid=(l+r)>>1;pushdown(p);
ll ret=0;
if(L<=mid)ret+=query(ls,l,mid,L,R);
if(R>mid)ret+=query(rs,mid+1,r,L,R);
return ret;
}
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
int n,m,a[N],b[N];
vector<pii>q[N];
int sta[N],topa,stb[N],topb;
ll ans[N];
signed main(){
read(),n=read();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++)b[i]=read();
m=read();
for(int i=1,l,r;i<=m;i++){
l=read(),r=read();
q[r].push_back(mp(l,i));
}
build(1,1,n);
for(int i=1;i<=n;i++){
while(topa&&a[sta[topa]]<a[i]){
modify(1,1,n,sta[topa-1]+1,sta[topa],-a[sta[topa]],0);
topa--;
}
modify(1,1,n,sta[topa]+1,i,a[i],0);
sta[++topa]=i;
while(topb&&b[stb[topb]]<b[i]){
modify(1,1,n,stb[topb-1]+1,stb[topb],-b[stb[topb]],1);
topb--;
}
modify(1,1,n,stb[topb]+1,i,b[i],1);
stb[++topb]=i;
spread(1,(Tree){0,0,0,0,0,1,0,0,0,0,0});
for(pii qry:q[i])
ans[qry.se]=query(1,1,n,qry.fi,i);
}
for(int i=1;i<=m;i++)
printf("%llu\n",ans[i]);
return 0;
}