刷題總結——維護數列(NOI2005 bzoj1500 splay)
題目:
題目背景
NOI2005 DAY1 T2
題目描述
請寫一個程序,要求維護一個數列,支持以下 6 種操作:(請註意,格式欄中的下劃線‘_’表示實際輸入文件中的空格)
輸入格式
第 1 行包含兩個數 N 和 M ,N 表示初始時數列中數的個數,M 表示要進行的操作數目。
第 2 行包含 N 個數字,描述初始時的數列。
以下 M 行,每行一條命令,格式參見問題描述中的表格。
輸出格式
對於輸入數據中的 GET-SUM 和 MAX-SUM 操作,向輸出文件依次打印結果,每個答案(數字)占一行。
樣例數據 1
輸入 [復制]
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
輸出
-1
10
1
10
備註
【樣例說明】
初始時,我們擁有數列:
2 -6 3 5 1 -5 -3 6 3
執行操作 GET-SUM 5 4 ,表示求出數列中從第 5 個數開始連續 4 個數字之和,如下圖中的灰色部分 1+(-5)+(-3)+6 = -1:
2 -6 3 5 1 -5 -3 6 3
執行操作 MAX-SUM ,表示要求求出當前數列中最大的一段和,即如下圖所示,應為 3+5+1+(-5)+(-3)+6+3 = 10:
2 -6 3 5 1 -5 -3 6 3
執行操作 INSERT 8 3 -5 7 2,即在數列中第 8 個數字後插入 -5 7 2,如下所示的灰色部分:
2 -6 3 5 1 -5 -3 6 -5 7 2
執行操作 DELETE 12 1,表示刪除第 12 個數字,即最後一個:
2 -6 3 5 1 -5 -3 6 -5 7 2
執行操作 MAKE-SAME 3 3 2 ,表示從第 3 個數開始的 3 個數字,即下圖中的灰色部分,統一修改為 2 :
2 -6 3 5 1 -5 -3 6 -5 7 2
改為
2 -6 2 2 2 -5 -3 6 -5 7 2
執行操作 REVERSE 3 6,表示取出數列中從第 3 個數開始的連續 6 個數:
如上所示的灰色部分 2 2 2 -5 -3 6 ,翻轉後得到 6 -3 -5 2 2 2 ,並放回原來位置:
2 -6 6 -3 -5 2 2 2 -5 7 2
最後執行 GET-SUM 5 4 和 MAX-SUM ,不難得到答案 1 和 10 。
【評分方法】
本題設有部分分,對於每一個測試點:
- 如果你的程序能在輸出文件正確的位置上打印 GET-SUM 操作的答案,你可以得到該測試點 60% 的分數;
- 如果你的程序能在輸出文件正確的位置上打印 MAX-SUM 操作的答案,你可以得到該測試點 40% 的分數;
- 以上兩條的分數可以疊加,即如果你的程序正確輸出所有 GET-SUM 和 MAX-SUM 操作的答案,你可以得到該測試點 100% 的分數。
請註意:如果你的程序只能正確處理某一種操作,請確定在輸出文件正確的位置上打印結果,即必須為另一種操作留下對應的行,否則我們不保證可以正確評分。
【數據規模和約定】
你可以認為在任何時刻,數列中至少有 1 個數。
輸入數據一定是正確的,即指定位置的數在數列中一定存在。
50% 的數據中,任何時刻數列中最多含有 30,000 個數;
100% 的數據中,任何時刻數列中最多含有 500,000 個數。
100% 的數據中,任何時刻數列中任何一個數字均在 [-1,000,1,000] 內 。
100% 的數據中,M≤20,000,插入的數字總數不超過 4,000,000 個,輸入文件大小不超過 20 MBytes 。
題解:
splay大boss題····能打得來splay操作基本沒問題了·····
維護lm,rm,mx,sum,size分別表示包含該節點子樹代表的區間的左端點的最大序列和,包含該節點子樹代表的區間右端點的最大序列和,totsum:此區間的最大序列和最大值,以及該節點子樹的和的總和和該節點子樹的大小
然後就是各種提取區間操作了,在此引用Zig_zag題解,%%%%
註:標準結構:區間右端點+1(R)為根,區間左端點-1(L)為根的左兒子,這樣目標區間就是L的右兒子,這種形式以後都用"標準結構"代替。
插入操作:先把需要插入的序列建成一個小平衡樹(遞歸),轉出標準結構,插到L的右兒子上就行了。
刪除操作:轉出標準結構,把L的右兒子切下來就行了(註意因為要回收空間,所以還是把要切的子樹遍歷了一遍,把這顆樹上的節點標號入棧)。
覆蓋操作:轉出標準結構,把L的右兒子打上覆蓋標記cov(以後下傳的時候把節點的值改為cov的值,sum變為cov*size,la=ra=ma變為cov和sum中較大的一個,因為有負數的情況)
翻轉操作:轉出標準結構,把L的右兒子打上翻轉標記rev(以後下傳的時候要交換左右兒子並且交換la和ra)
求和操作:轉出標準結構,答案就是L的右兒子的sum
最大值操作:轉出標準結構,答案就是L的右兒子的ma
區間操作的時候一定要明白一點,就是打標記的同時做修改,就是說當一個點帶了標記的時候,它已經被修改過了。
代碼:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<algorithm> #include<cctype> using namespace std; const int N=1000005; const int inf=0x3f3f3f3f; int ans,n,m,father[N],son[N][2],rev[N],sum[N],lm[N],rm[N],mx[N],cov[N],size[N],key[N],a[N],que[N],tail,tot,st,ed,root; char s[25]; inline int R() { char c;int f=0,i=1; for(c=getchar();(c<‘0‘||c>‘9‘)&&c!=‘-‘;c=getchar()); if(c==‘-‘) i=-1,c=getchar(); for(;c<=‘9‘&&c>=‘0‘;c=getchar()) f=(f<<3)+(f<<1)+c-‘0‘; return f*i; } void update(int x) { if (!x) return; lm[x]=max(lm[son[x][0]],sum[son[x][0]]+key[x]+max(0,lm[son[x][1]])); rm[x]=max(rm[son[x][1]],sum[son[x][1]]+key[x]+max(0,rm[son[x][0]])); mx[x]=max(max(mx[son[x][0]],mx[son[x][1]]),key[x]+max(0,rm[son[x][0]])+max(0,lm[son[x][1]])); sum[x]=sum[son[x][0]]+sum[son[x][1]]+key[x]; size[x]=size[son[x][0]]+size[son[x][1]]+1; } inline void reverse(int now) { if(!now) return; swap(son[now][1],son[now][0]); swap(lm[now],rm[now]); rev[now]^=1; } inline void cover(int now,int v) { if(!now) return; sum[now]=size[now]*v;key[now]=cov[now]=v; lm[now]=rm[now]=mx[now]=max(sum[now],v); } void pushdown(int x) { if (!x) return; if (rev[x]) { reverse(son[x][0]); reverse(son[x][1]); rev[x]=0; } if (cov[x]!=-inf) { cover(son[x][0],cov[x]); cover(son[x][1],cov[x]); cov[x]=-inf; } } inline int get(int now) { return son[father[now]][1]==now; } inline void rotate(int now) { int fa=father[now],ofa=father[fa],which=get(now); son[fa][which]=son[now][which^1],father[son[fa][which]]=fa; son[now][which^1]=fa,father[fa]=now,father[now]=ofa; if(ofa) son[ofa][son[ofa][1]==fa]=now; update(fa),update(now); } inline void relax(int now,int to) { if(now!=to) relax(father[now],to); pushdown(now); } inline void splay(int now,int to) { relax(now,to); while(father[now]!=to) { if(father[father[now]]!=to) rotate(get(now)==get(father[now])?father[now]:now); rotate(now); } if(!to) root=now; } inline int pick() { if(tail) return que[tail--]; else return ++tot; } int produce(int x) { int t=pick(); key[t]=a[x]; cov[t]=-inf; rev[t]=0; lm[t]=rm[t]=mx[t]=-inf; return t; } inline int find(int now,int k) { pushdown(now); if(size[son[now][0]]>=k) return find(son[now][0],k); else if(size[son[now][0]]+1==k) return now; else return find(son[now][1],k-size[son[now][0]]-1); } int build(int l,int r) { int mid=(l+r)>>1,le=0,ri=0; if (l<mid) le=build(l,mid-1); int t=produce(mid); if (r>mid) ri=build(mid+1,r); if (le) son[t][0]=le,father[le]=t; if (ri) son[t][1]=ri,father[ri]=t; update(t); return t; } void del(int &x) { if (!x) return; que[++tail]=x; father[x]=0; del(son[x][0]); del(son[x][1]); lm[x]=rm[x]=mx[x]=-inf; x=0; } int main() { //freopen("a.in","r",stdin); n=R();m=R();a[st=1]=0;a[ed=n+2]=0; for(int i=2;i<=n+1;i++) a[i]=R(); rm[0]=lm[0]=mx[0]=-inf; root=build(1,n+2);int x,y,z,l,r; for (int i=1;i<=m;i++) { scanf("%s",s); if (s[0]==‘I‘) { x=R(),y=R(); l=find(root,x+1); r=find(root,x+2); splay(r,0); splay(l,root); for (int j=1;j<=y;j++) a[j]=R(); int tmp=build(1,y); father[tmp]=l; son[l][1]=tmp; update(l); update(r); splay(tmp,root); } if (s[0]==‘D‘) { x=R(),y=R(); r=find(root,x+y+1);splay(r,0); l=find(root,x);splay(l,root); del(son[l][1]); update(l); update(r); } if (s[0]==‘M‘&&s[2]==‘K‘) { x=R(),y=R(),z=R(); r=find(root,x+y+1);splay(r,0); l=find(root,x);splay(l,root); cover(son[l][1],z); } if (s[0]==‘R‘) { x=R(),y=R(); r=find(root,x+y+1);splay(r,0); l=find(root,x);splay(l,root); reverse(son[l][1]); } if (s[0]==‘G‘) { x=R(),y=R(); r=find(root,x+y+1);splay(r,0); l=find(root,x);splay(l,root); ans=sum[son[l][1]]; printf("%d\n",ans); } if (s[0]==‘M‘&&s[2]==‘X‘) { splay(ed,0); splay(st,root); ans=mx[son[st][1]]; printf("%d\n",ans); } } return 0; }
刷題總結——維護數列(NOI2005 bzoj1500 splay)