BZOJ1500 [NOI2005]维修数列(Splay tree)
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
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
10
1
10
HINT
题解:Splay tree模板题;
自己的板子:
1 #include<bits/stdc++.h> 2 #define RI register int 3 #define For(i,a,b) for (RI i=a;i<=b;++i) 4 using namespace std; 5 const int inf=0x3f3f3f3f; 6 const int N=1e6+17; 7 int n,m,rt,cnt; 8 int a[N],id[N],fa[N],c[N][2]; 9 int sum[N],sz[N],v[N],mx[N],lx[N],rx[N]; 10 bool tag[N],rev[N]; 11 //tag表示是否有统一修改的标记,rev表示是否有统一翻转的标记 12 //sum表示这个点的子树中的权值和,v表示这个点的权值 13 //lx[]是一个子树以最左端为起点向右延伸的最大子串和,rx类似 14 //mx[]是当前子树的最大子串和 15 queue<int> q; 16 inline int read() 17 { 18 RI x=0,f=1;char ch=getchar(); 19 while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} 20 while('0'<=ch&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} 21 return x*f; 22 } 23 inline void pushup(RI x) 24 { 25 RI l=c[x][0],r=c[x][1]; 26 sum[x]=sum[l]+sum[r]+v[x]; 27 sz[x]=sz[l]+sz[r]+1; 28 mx[x]=max(mx[l],max(mx[r],rx[l]+v[x]+lx[r])); 29 lx[x]=max(lx[l],sum[l]+v[x]+lx[r]); 30 rx[x]=max(rx[r],sum[r]+v[x]+rx[l]); 31 } 32 //上传记录标记 33 inline void pushdown(RI x) 34 { 35 RI l=c[x][0],r=c[x][1]; 36 if(tag[x]) 37 { 38 rev[x]=tag[x]=0;//我们有了一个统一修改的标记,再翻转就没有什么意义了 39 if(l) tag[l]=1,v[l]=v[x],sum[l]=v[x]*sz[l]; 40 if(r) tag[r]=1,v[r]=v[x],sum[r]=v[x]*sz[r]; 41 if(v[x]>=0) 42 { 43 if(l) lx[l]=rx[l]=mx[l]=sum[l]; 44 if(r) lx[r]=rx[r]=mx[r]=sum[r]; 45 } 46 else 47 { 48 if(l) lx[l]=rx[l]=0,mx[l]=v[x]; 49 if(r) lx[r]=rx[r]=0,mx[r]=v[x]; 50 } 51 } 52 if(rev[x]) 53 { 54 rev[x]=0;rev[l]^=1;rev[r]^=1; 55 swap(lx[l],rx[l]);swap(lx[r],rx[r]); 56 //注意,在翻转操作中,前后缀的最大和子序列都反过来了 57 swap(c[l][0],c[l][1]);swap(c[r][0],c[r][1]); 58 } 59 } 60 inline void rotate(RI x,RI &k) 61 { 62 RI y=fa[x],z=fa[y],l=(c[y][1]==x),r=l^1; 63 if (y==k)k=x;else c[z][c[z][1]==y]=x; 64 fa[c[x][r]]=y;fa[y]=x;fa[x]=z; 65 c[y][l]=c[x][r];c[x][r]=y; 66 pushup(y);pushup(x); 67 //旋转操作,一定要上传标记且顺序不能变 68 } 69 inline void splay(RI x,RI &k) 70 { 71 while(x!=k) 72 { 73 int y=fa[x],z=fa[y]; 74 if(y!=k) 75 { 76 if((c[z][0]==y)^(c[y][0]==x)) rotate(x,k); 77 else rotate(y,k); 78 } 79 rotate(x,k); 80 } 81 } 82 //这是整个程序的核心之一,毕竟是伸展操作嘛 83 inline int find(RI x,RI rk) 84 {//返回当前序列第rk个数的标号 85 pushdown(x); 86 RI l=c[x][0],r=c[x][1]; 87 if(sz[l]+1==rk) return x; 88 if(sz[l]>=rk) return find(l,rk); 89 else return find(r,rk-sz[l]-1); 90 } 91 inline void recycle(RI x) 92 {//这就是用时间换空间的回收冗余编号机制,很好理解 93 RI &l=c[x][0],&r=c[x][1]; 94 if(l) recycle(l); 95 if(r) recycle(r); 96 q.push(x); 97 fa[x]=l=r=tag[x]=rev[x]=0; 98 } 99 inline int split(RI k,RI tot)//找到[k+1,k+tot] 100 { 101 RI x=find(rt,k),y=find(rt,k+tot+1); 102 splay(x,rt);splay(y,c[x][1]); 103 return c[y][0]; 104 } 105 //这个split操作是整个程序的核心之三 106 //我们通过这个split操作,找到[k+1,k+tot],并把k,和k+tot+1移到根和右儿子的位置 107 //然后我们返回了这个右儿子的左儿子,这就是我们需要操作的区间 108 inline void query(RI k,RI tot) 109 { 110 RI x=split(k,tot); 111 printf("%d\n",sum[x]); 112 } 113 inline void modify(RI k,RI tot,RI val)//MAKE-SAME 114 { 115 RI x=split(k,tot),y=fa[x]; 116 v[x]=val;tag[x]=1;sum[x]=sz[x]*val; 117 if(val>=0) lx[x]=rx[x]=mx[x]=sum[x]; 118 else lx[x]=rx[x]=0,mx[x]=val; 119 pushup(y);pushup(fa[y]); 120 //每一步的修改操作,由于父子关系发生改变 121 //及记录标记发生改变,我们需要及时上传记录标记 122 } 123 inline void rever(RI k,RI tot)//翻转 124 { 125 RI x=split(k,tot),y=fa[x]; 126 if(!tag[x]) 127 { 128 rev[x]^=1; 129 swap(c[x][0],c[x][1]); 130 swap(lx[x],rx[x]); 131 pushup(y);pushup(fa[y]); 132 } 133 //同上 134 } 135 inline void erase(RI k,RI tot)//DELETE 136 { 137 RI x=split(k,tot),y=fa[x]; 138 recycle(x);c[y][0]=0; 139 pushup(y);pushup(fa[y]); 140 //同上 141 } 142 inline void build(RI l,RI r,RI f) 143 { 144 RI mid=(l+r)>>1,now=id[mid],pre=id[f]; 145 if(l==r) 146 { 147 mx[now]=sum[now]=a[l]; 148 tag[now]=rev[now]=0; 149 //这里这个tag和rev的清0是必要,因为这个编号可能是之前冗余了 150 lx[now]=rx[now]=max(a[l],0); 151 sz[now]=1; 152 } 153 if(l<mid) build(l,mid-1,mid); 154 if(mid<r) build(mid+1,r,mid); 155 v[now]=a[mid]; fa[now]=pre; 156 pushup(now); //上传记录标记 157 c[pre][mid>=f]=now; 158 //当mid>=f时,now是插入到又区间取了,所以c[pre][1]=now,当mid<f时同理 159 } 160 inline void insert(RI k,RI tot) 161 { 162 for(int i=1;i<=tot;++i) a[i]=read(); 163 for(int i=1;i<=tot;++i) 164 { 165 if(!q.empty()) id[i]=q.front(),q.pop(); 166 else id[i]=++cnt;//利用队列中记录的冗余节点编号 167 } 168 build(1,tot,0); 169 RI z=id[(1+tot)>>1]; 170 RI x=find(rt,k+1),y=find(rt,k+2); 171 //首先,依据中序遍历,找到我们需要操作的区间的实际编号 172 splay(x,rt);splay(y,c[x][1]); 173 //把k+1(注意我们已经右移了一个单位)和(k+1)+1移到根和右儿子 174 fa[z]=y;c[y][0]=z; 175 //直接把需要插入的这个平衡树挂到右儿子的左儿子上去就好了 176 pushup(y);pushup(x); 177 //上传记录标记 178 } 179 //可以这么记,只要用了split就要重新上传标记 180 //只有find中需要下传标记 181 int main() 182 { 183 n=read(),m=read(); 184 mx[0]=a[1]=a[n+2]=-inf; 185 For(i,1,n) a[i+1]=read(); 186 For(i,1,n+2) id[i]=i;//虚拟了两个节点1和n+2,然后把需要操作区间整体右移一个单位 187 build(1,n+2,0);//建树 188 rt=(n+3)>>1;cnt=n+2;//取最中间的为根 189 RI k,tot,val;char ch[10]; 190 while(m--) 191 { 192 scanf("%s",ch); 193 if(ch[0]!='M' || ch[2]!='X') k=read(),tot=read(); 194 if(ch[0]=='I') insert(k,tot); 195 if(ch[0]=='D') erase(k,tot);//DELETE 196 if(ch[0]=='M') 197 { 198 if(ch[2]=='X') printf("%d\n",mx[rt]);//MAX-SUM 199 else val=read(),modify(k,tot,val);//MAKE-SAME 200 } 201 if(ch[0]=='R') rever(k,tot);//翻转 202 if(ch[0]=='G') query(k,tot);//GET-SUM 203 } 204 return 0; 205 }
斌神的板子
1 /************************************************************** 2 Problem: 1500 3 User: SongHL 4 Language: C++ 5 Result: Accepted 6 Time:6152 ms 7 Memory:26684 kb 8 ****************************************************************/ 9 10 #include<bits/stdc++.h> 11 using namespace std; 12 13 #define Key_value ch[ch[root][1]][0] 14 const int MAXN = 500010; 15 const int INF = 0x3f3f3f3f; 16 int pre[MAXN],ch[MAXN][2],key[MAXN],size[MAXN]; 17 int root,tot1; 18 int sum[MAXN],rev[MAXN],same[MAXN]; 19 int lx[MAXN],rx[MAXN],mx[MAXN]; 20 int s[MAXN],tot2;//内存池和容量 21 int a[MAXN]; 22 int n,q; 23 24 void NewNode(int &r,int father,int k) 25 { 26 if(tot2) r = s[tot2--];//取的时候是tot2--,存的时候就是++tot2 27 else r = ++tot1; 28 pre[r] = father; 29 ch[r][0] = ch[r][1] = 0; 30 key[r] = k; 31 sum[r] = k; 32 rev[r] = same[r] = 0; 33 lx[r] = rx[r] = mx[r] = k; 34 size[r] = 1; 35 } 36 void Update_Rev(int r) 37 { 38 if(!r)return; 39 swap(ch[r][0],ch[r][1]); 40 swap(lx[r],rx[r]); 41 rev[r] ^= 1; 42 } 43 void Update_Same(int r,int v) 44 { 45 if(!r)return; 46 key[r] = v; 47 sum[r] = v*size[r]; 48 lx[r] = rx[r] = mx[r] = max(v,v*size[r]); 49 same[r] = 1; 50 } 51 void push_up(int r) 52 { 53 int lson = ch[r][0], rson = ch[r][1]; 54 size[r] = size[lson] + size[rson] + 1; 55 sum[r] = sum[lson] + sum[rson] + key[r]; 56 lx[r] = max(lx[lson],sum[lson] + key[r] + max(0,lx[rson])); 57 rx[r] = max(rx[rson],sum[rson] + key[r] + max(0,rx[lson])); 58 mx[r] = max(0,rx[lson]) + key[r] + max(0,lx[rson]); 59 mx[r] = max(mx[r],max(mx[lson],mx[rson])); 60 } 61 void push_down(int r) 62 { 63 if(same[r]) 64 { 65 Update_Same(ch[r][0],key[r]); 66 Update_Same(ch[r][1],key[r]); 67 same[r] = 0; 68 } 69 if(rev[r]) 70 { 71 Update_Rev(ch[r][0]); 72 Update_Rev(ch[r][1]); 73 rev[r] = 0; 74 } 75 } 76 void Build(int &x,int l,int r,int father) 77 { 78 if(l > r)return; 79 int mid = (l+r)/2; 80 NewNode(x,father,a[mid]); 81 Build(ch[x][0],l,mid-1,x); 82 Build(ch[x][1],mid+1,r,x); 83 push_up(x); 84 } 85 void Init() 86 { 87 root = tot1 = tot2 = 0; 88 ch[root][0] = ch[root][1] = size[root] = pre[root] = 0; 89 same[root] = rev[root] = sum[root] = key[root] = 0; 90 lx[root] = rx[root] = mx[root] = -INF; 91 NewNode(root,0,-1); 92 NewNode(ch[root][1],root,-1); 93 for(int i = 0;i < n;i++) 94 scanf("%d",&a[i]); 95 Build(Key_value,0,n-1,ch[root][1]); 96 push_up(ch[root][1]); 97 push_up(root); 98 } 99 //旋转,0为左旋,1为右旋 100 void Rotate(int x,int kind) 101 { 102 int y = pre[x]; 103 push_down(y); 104 push_down(x); 105 ch[y][!kind] = ch[x][kind]; 106 pre[ch[x][kind]] = y; 107 if(pre[y]) 108 ch[pre[y]][ch[pre[y]][1]==y] = x; 109 pre[x] = pre[y]; 110 ch[x][kind] = y; 111 pre[y] = x; 112 push_up(y); 113 } 114 //Splay调整,将r结点调整到goal下面 115 void Splay(int r,int goal) 116 { 117 push_down(r); 118 while(pre[r] != goal) 119 { 120 if(pre[pre[r]] == goal) 121 { 122 push_down(pre[r]); 123 push_down(r); 124 Rotate(r,ch[pre[r]][0] == r); 125 } 126 else 127 { 128 push_down(pre[pre[r]]); 129 push_down(pre[r]); 130 push_down(r); 131 int y = pre[r]; 132 int kind = ch[pre[y]][0]==y; 133 if(ch[y][kind] == r) 134 { 135 Rotate(r,!kind); 136 Rotate(r,kind); 137 } 138 else 139 { 140 Rotate(y,kind); 141 Rotate(r,kind); 142 } 143 } 144 } 145 push_up(r); 146 if(goal == 0) root = r; 147 } 148 int Get_kth(int r,int k) 149 { 150 push_down(r); 151 int t = size[ch[r][0]] + 1; 152 if(t == k)return r; 153 if(t > k)return Get_kth(ch[r][0],k); 154 else return Get_kth(ch[r][1],k-t); 155 } 156 157 //在第pos个数后面插入tot个数 158 void Insert(int pos,int tot) 159 { 160 for(int i = 0;i < tot;i++)scanf("%d",&a[i]); 161 Splay(Get_kth(root,pos+1),0); 162 Splay(Get_kth(root,pos+2),root); 163 Build(Key_value,0,tot-1,ch[root][1]); 164 push_up(ch[root][1]); 165 push_up(root); 166 } 167 168 //删除子树 169 void erase(int r) 170 { 171 if(!r)return; 172 s[++tot2] = r; 173 erase(ch[r][0]); 174 erase(ch[r][1]); 175 } 176 //从第pos个数开始连续删除tot个数 177 void Delete(int pos,int tot) 178 { 179 Splay(Get_kth(root,pos),0); 180 Splay(Get_kth(root,pos+tot+1),root); 181 erase(Key_value); 182 pre[Key_value] = 0; 183 Key_value = 0; 184 push_up(ch[root][1]); 185 push_up(root); 186 } 187 //将从第pos个数开始的连续的tot个数修改为c 188 void Make_Same(int pos,int tot,int c) 189 { 190 Splay(Get_kth(root,pos),0); 191 Splay(Get_kth(root,pos+tot+1),root); 192 Update_Same(Key_value,c); 193 push_up(ch[root][1]); 194 push_up(root); 195 } 196 197 //将第pos个数开始的连续tot个数进行反转 198 void Reverse(int pos,int tot) 199 { 200 Splay(Get_kth(root,pos),0); 201 Splay(Get_kth(root,pos+tot+1),root); 202 Update_Rev(Key_value); 203 push_up(ch[root][1]); 204 push_up(root); 205 } 206 //得到第pos个数开始的tot个数的和 207 int Get_Sum(int pos,int tot) 208 { 209 Splay(Get_kth(root,pos),0); 210 Splay(Get_kth(root,pos+tot+1),root); 211 return sum[Key_value]; 212 } 213 //得到第pos个数开始的tot个数中最大的子段和 214 int Get_MaxSum(int pos,int tot) 215 { 216 Splay(Get_kth(root,pos),0); 217 Splay(Get_kth(root,pos+tot+1),root); 218 return mx[Key_value]; 219 } 220 221 void InOrder(int r) 222 { 223 if(!r)return; 224 push_down(r); 225 InOrder(ch[r][0]); 226 printf("%d ",key[r]); 227 InOrder(ch[r][1]); 228 } 229 230 231 232 int main() 233 { 234 while(scanf("%d%d",&n,&q) == 2) 235 { 236 Init(); 237 char op[20]; 238 int x,y,z; 239 while(q--) 240 { 241 scanf("%s",op); 242 if(strcmp(op,"INSERT") == 0) 243 { 244 scanf("%d%d",&x,&y); 245 Insert(x,y); 246 } 247 else if(strcmp(op,"DELETE") == 0) 248 { 249 scanf("%d%d",&x,&y); 250 Delete(x,y); 251 } 252 else if(strcmp(op,"MAKE-SAME") == 0) 253 { 254 scanf("%d%d%d",&x,&y,&z); 255 Make_Same(x,y,z); 256 } 257 else if(strcmp(op,"REVERSE") == 0) 258 { 259 scanf("%d%d",&x,&y); 260 Reverse(x,y); 261 } 262 else if(strcmp(op,"GET-SUM") == 0) 263 { 264 scanf("%d%d",&x,&y); 265 printf("%d\n",Get_Sum(x,y)); 266 } 267 else if(strcmp(op,"MAX-SUM") == 0) 268 printf("%d\n",Get_MaxSum(1,size[root]-2)); 269 } 270 } 271 return 0; 272 }