1
給出 個數, 次詢問,求區間最大子段和,相同的數只算一次。(GSS2)
在線不是很好做,我們離線下來,把詢問按 升序排序,線段樹上維護每個 到 的和。如果預處理出每個數上一個出現的位置 ,那麼每次右端點右移相當於區間 加 ,查詢的時候就成了查詢區間歷史最大值。也就是:
1.區間加
2.區間歷史最大值
只需要在線段樹上多維護一個歷史最大值標記和歷史最大值,歷史最大值標記表示的是當前節點從上次標記下放到現在,修改標記的最大值。每次 push_down 的時候用根的歷史最大值標記和子節點的當前值嘗試更新子節點的歷史最大值。
void push_down(int root)
{
int ls=root<<1,rs=root<<1|1;
if(padd[root])
{
pMax[ls]=max(pMax[ls],padd[root]+Max[ls]);
padd[ls]=max(padd[ls],padd[root]+add[ls]);
pMax[rs]=max(pMax[rs],padd[root]+Max[rs]);
padd[rs]=max(padd[rs],padd[root]+add[rs]);
padd[root]=0;
}
if(add[root])
{
Max[ls]+=add[root],add[ls]+=add[root];
Max[rs]+=add[root],add[rs]+=add[root];
add[root]=0;
}
}
void push_up(int root)
{
int ls=root<<1,rs=root<<1|1;
Max[root]=max(Max[ls],Max[rs]); //當前最大值
pMax[root]=max(pMax[ls],pMax[rs]); //歷史最大值
}
void update(int root,int l,int r,int x,int y,int k)
{
if(x<=l&&y>=r)
{
add[root]+=k;
Max[root]+=k;
padd[root]=max(padd[root],add[root]); //歷史最大值標記
pMax[root]=max(pMax[root],Max[root]);
return;
}
push_down(root);
int mid=l+r>>1;
if(x<=mid) update(root<<1,l,mid,x,y,k);
if(y>mid) update(root<<1|1,mid+1,r,x,y,k);
push_up(root);
}
看起來還是很友好。
2
維護一個序列,支持:
1.區間加。
2.區間賦值。
3.區間所有數對 取 max。
3.查詢區間和。
4.查詢區間歷史最大值。
開始鬼畜起來了…
首先對於區間取 max,我們在線段樹上維護區間最小值、區間次小值以及最小值出現的次數。如果 小於等於區間最小值,直接返回;如果 大於區間最小值且小於次小值,就修改區間信息返回;否則遞歸兩個兒子。玄學的勢能分析證明覆雜度是 的,並且可以和區間加、區間賦值、區間取 min 等操作組合。
對於其他操作,觀察到可以簡化爲兩個操作:區間加,區間取 max。如果用一個二元組表示,那麼三個修改分別對應 。因此,線段樹上的標記可以用這樣的二元組表示。同樣,我們需要維護一個當前標記和“歷史最大修改標記”。
當前標記怎麼合併?例如 和 合併,我們能得到 。
歷史最大值標記呢?類比上一題,如果父節點的歷史最大標記是 ,節點的當前標記爲 ,歷史最大標記是 ,我們就用 去嘗試更新 ,方法是對兩維分別取 max。
給出沒有詢問區間和的代碼(洛谷P4314)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct node{
int x,y;
}ntag[400010],ptag[400010],tmp;
const int INF=1e9;
char s[10];
int Max[400010],pMax[400010],a[100010];
node operator | (node a,node b) {return node{max(a.x,b.x),max(a.y,b.y)};}
node operator * (node a,node b) {return node{max(-INF,a.x+b.x),max(a.y+b.x,b.y)};}
int operator + (int a,node b) {return max(a+b.x,b.y);}
inline int read()
{
char c=getchar();int x=0,flag=1;
while(!isdigit(c)){if(c=='-') flag=-1;c=getchar();}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x*flag;
}
void push_up(int root)
{
int ls=root<<1,rs=root<<1|1;
Max[root]=max(Max[ls],Max[rs]);
pMax[root]=max(pMax[ls],pMax[rs]);
}
void push_down(int root)
{
int ls=root<<1,rs=root<<1|1;
ptag[ls]=ptag[ls]|(ntag[ls]*ptag[root]);
ntag[ls]=ntag[ls]*ntag[root];
pMax[ls]=max(pMax[ls],Max[ls]+ptag[root]);
Max[ls]=Max[ls]+ntag[root];
ptag[rs]=ptag[rs]|(ntag[rs]*ptag[root]);
ntag[rs]=ntag[rs]*ntag[root];
pMax[rs]=max(pMax[rs],Max[rs]+ptag[root]);
Max[rs]=Max[rs]+ntag[root];
ntag[root]=ptag[root]=node{0,-INF};
}
void build(int root,int l,int r)
{
ntag[root]=ptag[root]=node{0,-INF};
if(l==r)
{
Max[root]=pMax[root]=a[l];
return;
}
int mid=l+r>>1;
build(root<<1,l,mid);
build(root<<1|1,mid+1,r);
push_up(root);
}
void update(int root,int l,int r,int x,int y)
{
if(x<=l&&y>=r)
{
ntag[root]=ntag[root]*tmp;
ptag[root]=ptag[root]|ntag[root];
Max[root]=max(Max[root]+tmp.x,tmp.y);
pMax[root]=max(pMax[root],Max[root]);
return;
}
int mid=l+r>>1;
push_down(root);
if(x<=mid) update(root<<1,l,mid,x,y);
if(y>mid) update(root<<1|1,mid+1,r,x,y);
push_up(root);
}
int query(int root,int l,int r,int x,int y,int t)
{
if(x<=l&&y>=r) return t?pMax[root]:Max[root];
int mid=l+r>>1,ans=-INF;
push_down(root);
if(x<=mid) ans=max(ans,query(root<<1,l,mid,x,y,t));
if(y>mid) ans=max(ans,query(root<<1|1,mid+1,r,x,y,t));
push_up(root);
return ans;
}
int main()
{
int n=read();
for(int i=1;i<=n;i++) a[i]=read();
build(1,1,n);
int m=read();
for(int i=1;i<=m;i++)
{
scanf("%s",s);
int x=read(),y=read();
if(s[0]=='Q') cout<<query(1,1,n,x,y,0)<<'\n';
else if(s[0]=='A') cout<<query(1,1,n,x,y,1)<<'\n';
else
{
int z=read();
if(s[0]=='P') tmp=node{z,-INF};
else tmp=node{-INF,z};
update(1,1,n,x,y);
}
}
return 0;
}