1. 程式人生 > >BZOJ 1500/Luogu 2042 - 維修數列 - [NOI2005][Splay]

BZOJ 1500/Luogu 2042 - 維修數列 - [NOI2005][Splay]

題目連結:https://www.lydsy.com/JudgeOnline/problem.php?id=1500

題目連結:https://www.luogu.org/problemnew/show/P2042

Description

請寫一個程式,要求維護一個數列,支援以下 6 種操作: 請注意,格式欄 中的下劃線‘ _ ’表示實際輸入檔案中的空格

Input

輸入的第1 行包含兩個數N 和M(M ≤20 000),N 表示初始時數列中數的個數,M表示要進行的運算元目。

第2行包含N個數字,描述初始時的數列。

以下M行,每行一條命令,格式參見問題描述中的表格。

任何時刻數列中最多含有500 000個數,數列中任何一個數字均在[-1 000, 1 000]內。

插入的數字總數不超過4 000 000個,輸入檔案大小不超過20MBytes。

Output

對於輸入資料中的GET-SUM和MAX-SUM操作,向輸出檔案依次列印結果,每個答案(數字)佔一行。

Sample Input
9 8
2 -6 3 5 1 -5 -3 6 3
GET-SUM 5 4
MAX-SUM
INSERT 8 3 -5 7 2
DELETE 12 1
MAKE-SAME 3 3 2
REVERSE 3 6
GET-SUM 5 4
MAX-SUM

Sample Output
-1
10
1
10

HINT

 

題解:

Splay模板題。

其中,關於如何搞定求區間最大連續子列和的問題,可以參考線段樹的做法:

UVALive 3938 - "Ray, Pass me the dishes!" - [最大連續子列和+線段樹](通過分治+最大字首和+最大字尾和共同維護得到最大連續子列和)(感慨一下,已經想不起是哪個時候做的這道題了,時光飛逝啊……)。

關於區間翻轉,則是Splay老生常談的事情了,一個 $rev$ 標記搞定。

 

AC程式碼:

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=5e5+10;

int n,m;
int a[maxn];

/******************************** splay - st ********************************/ #define Key_value ch[ch[root][1]][0] int root,nodecnt; int par[maxn],ch[maxn][2]; int key[maxn],sum[maxn],siz[maxn]; int mxpre[maxn],mxsuf[maxn],mxsub[maxn]; //最大字首和,最大字尾和,最大連續子列和 bool alt[maxn],rev[maxn]; //修改標記,反轉標記 int pool[maxn],poolsize; //節點回收 void NewNode(int &x,int p,int k) { if(poolsize>0) x=pool[--poolsize]; else x=++nodecnt; par[x]=p; ch[x][0]=ch[x][1]=0; key[x]=sum[x]=k; mxpre[x]=mxsuf[x]=mxsub[x]=k; siz[x]=1; alt[x]=rev[x]=0; } void Update_Rev(int x) { if(x==0) return; swap(ch[x][0],ch[x][1]); swap(mxpre[x],mxsuf[x]); rev[x]^=1; } void Update_Alt(int x,int val) { if(x==0) return; key[x]=val; sum[x]=siz[x]*val; mxpre[x]=mxsuf[x]=mxsub[x]=max(val,val*siz[x]); alt[x]=1; } void Pushup(int x) { int ls=ch[x][0],rs=ch[x][1]; siz[x]=siz[ls]+siz[rs]+1; sum[x]=sum[ls]+sum[rs]+key[x]; mxpre[x]=max(mxpre[ls],sum[ls]+key[x]+max(0,mxpre[rs])); mxsuf[x]=max(mxsuf[rs],max(0,mxsuf[ls])+key[x]+sum[rs]); mxsub[x]=max(max(mxsub[ls],mxsub[rs]),max(0,mxsuf[ls])+key[x]+max(0,mxpre[rs])); } void Pushdown(int x) { if(rev[x]) { Update_Rev(ch[x][0]); Update_Rev(ch[x][1]); rev[x]=0; } if(alt[x]) { Update_Alt(ch[x][0],key[x]); Update_Alt(ch[x][1],key[x]); alt[x]=0; } } void Rotate(int x,int type) //旋轉,0為左旋zag,1為右旋zig { int y=par[x]; ch[y][!type]=ch[x][type]; par[ch[x][type]]=y; if(par[y]) ch[par[y]][(ch[par[y]][1]==y)]=x; par[x]=par[y]; ch[x][type]=y; par[y]=x; Pushup(y); Pushup(x); } void Splay(int x,int goal) { while(par[x]!=goal) { if(par[par[x]]==goal) Rotate(x,ch[par[x]][0]==x); //左孩子zig,右孩子zag else { int y=par[x]; int type=(ch[par[y]][0]==y); //type=0,y是右孩子;type=1,y是左孩子 if(ch[y][type]==x) { Rotate(x,!type); Rotate(x,type); } else { Rotate(y,type); Rotate(x,type); } } } if(goal==0) root=x; } int Get_Kth(int x,int k) //得到第k個節點 { Pushdown(x); int t=siz[ch[x][0]]+1; if(t==k) return x; if(t>k) return Get_Kth(ch[x][0],k); else return Get_Kth(ch[x][1],k-t); } void Build(int &x,int l,int r,int par) //建樹,先建立中間結點,再建兩端的方法 { if(l>r) return; int mid=(l+r)/2; NewNode(x,par,a[mid]); Build(ch[x][0],l,mid-1,x); Build(ch[x][1],mid+1,r,x); Pushup(x); } void Init() //初始化,前後各加一個空節點 { root=nodecnt=poolsize=0; par[0]=ch[0][0]=ch[0][1]=0; key[0]=sum[0]=siz[0]=0; alt[0]=rev[0]=0; mxpre[0]=mxsuf[0]=mxsub[0]=-INF; NewNode(root,0,-INF); //頭部加入一個空位 NewNode(ch[root][1],root,-INF); //尾部加入一個空位 Build(Key_value,1,n,ch[root][1]); Pushup(ch[root][1]); Pushup(root); } void Insert(int p,int tot) { for(int i=1;i<=tot;i++) scanf("%d",&a[i]); Splay(Get_Kth(root,p+0+1),0); //p伸展到根 Splay(Get_Kth(root,p+1+1),root); //p的後繼p+1伸展到根的右孩子 Build(Key_value,1,tot,ch[root][1]); Pushup(ch[root][1]); Pushup(root); } void Collect(int x) //回收節點x統領的子樹 { if(x==0) return; pool[poolsize++]=x; Collect(ch[x][0]); Collect(ch[x][1]); } void Delete(int p,int tot) { Splay(Get_Kth(root,p-1+1),0); //伸展到根 Splay(Get_Kth(root,p+tot+1),root); //伸展到根的右孩子 Collect(Key_value); par[Key_value]=0; Key_value=0; Pushup(ch[root][1]); Pushup(root); } void Alter(int p,int tot,int c) //修改[p,p+tot)為k { Splay(Get_Kth(root,p-1+1),0); //伸展到根 Splay(Get_Kth(root,p+tot+1),root); //伸展到根的右孩子 Update_Alt(Key_value,c); Pushup(ch[root][1]); Pushup(root); } void Reverse(int p,int tot) //反轉[p,p+tot)區間 { Splay(Get_Kth(root,p-1+1),0); Splay(Get_Kth(root,p+tot+1),root); Update_Rev(Key_value); Pushup(ch[root][1]); Pushup(root); } int Get_Sum(int p,int tot) { Splay(Get_Kth(root,p-1+1),0); Splay(Get_Kth(root,p+tot+1),root); return sum[Key_value]; } int Get_MaxSub(int p,int tot) { Splay(Get_Kth(root,p-1+1),0); Splay(Get_Kth(root,p+tot+1),root); return mxsub[Key_value]; } /******************************** splay - ed ********************************/ int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); Init(); char op[12]; int pos,tot,c; while(m--) { scanf("%s",op); switch(op[0]&op[1]|op[2]) { case ('I'&'N'|'S'): scanf("%d%d",&pos,&tot); Insert(pos,tot); break; case ('D'&'E'|'L'): scanf("%d%d",&pos,&tot); Delete(pos,tot); break; case ('M'&'A'|'K'): scanf("%d%d%d",&pos,&tot,&c); Alter(pos,tot,c); break; case ('R'&'E'|'V'): scanf("%d%d",&pos,&tot); Reverse(pos,tot); break; case ('G'&'E'|'T'): scanf("%d%d",&pos,&tot); printf("%d\n",Get_Sum(pos,tot)); break; case ('M'&'A'|'X'): printf("%d\n",Get_MaxSub(1,siz[root]-2)); break; } } }