zoukankan      html  css  js  c++  java
  • BZOJ1500 [NOI2005]维修数列(Splay tree)

    [Submit][Status][Discuss]

    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 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
    ",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
    ",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 }
    View Code

    斌神的板子

      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
    ",Get_Sum(x,y));
    266             }
    267             else if(strcmp(op,"MAX-SUM") == 0)
    268                 printf("%d
    ",Get_MaxSum(1,size[root]-2));
    269         }
    270     }
    271     return 0;
    272 }
    View Code

      

     
  • 相关阅读:
    Java实现 LeetCode 394 字符串解码
    Java实现 LeetCode 394 字符串解码
    Java实现 LeetCode 392 判断子序列
    Java实现 LeetCode 392 判断子序列
    Java实现 LeetCode 392 判断子序列
    Java实现 LeetCode 391 完美矩形
    Java实现 LeetCode 391 完美矩形
    Java实现 LeetCode 391 完美矩形
    Java实现 LeetCode 390 消除游戏
    Java实现 LeetCode 390 消除游戏
  • 原文地址:https://www.cnblogs.com/csushl/p/9808010.html
Copyright © 2011-2022 走看看