• [NOI2005]维修数列 Splay tree 区间反转,修改,求和,求最值


      题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1500

    Description

    Input

    输入文件的第1行包含两个数N和M,N表示初始时数列中数的个数,M表示要进行的操作数目。 第2行包含N个数字,描述初始时的数列。 以下M行,每行一条命令,格式参见问题描述中的表格。

    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

      反转和求和操作很好求,直接用两个标记rev和sum懒惰求值。对于区间最大值,每个区间需要维护3个信息:左端点开始的最大值,右端点的最大值,区间最大值。Push_Up

    根据这三个值来就可以了,很好推。。。

      TlE+WA很多次,有很多要注意的地方。。。

      首先,Insert操作有很多,会使用过多的空间(大概100MB),如果不回收空间,会超时,我们可以人工压一个栈回收删除的节点。。。

      在初始化根节点的虚拟父亲节点0的时候,sum初始化为0,maxl、maxm和maxr需要初始化为-INF,因为如果一个节点没有两个儿子,那么会通过0节点来更新。

      Update_Same操作要注意如果节点不存在则不要更新,不然会影响0号节点。rev操作不仅仅要交换左右节点,还要交换左右最值。。。

      因为这里涉及反转和求最值操作,因此反转操作的延迟更新需要快于求最值的更新。。。

      基本这些问题都注意了,就差不多了。。。

      我开始在求MAX-SUM的时候是把初始增加的两个节点val初始化为0,然后用Push_Up维护更新信息,那么直接输出maxm[root]就可以了,但是WA了T^T。。。但是改为Splay()来维护,然后输出maxm[Key_value]就能A了(见代码上的注释),郁闷得死啊,求大神解释><..

      1 //STATUS:C++_AC_6060MS_23736KB
      2 #include <functional>
      3 #include <algorithm>
      4 #include <iostream>
      5 //#include <ext/rope>
      6 #include <fstream>
      7 #include <sstream>
      8 #include <iomanip>
      9 #include <numeric>
     10 #include <cstring>
     11 #include <cassert>
     12 #include <cstdio>
     13 #include <string>
     14 #include <vector>
     15 #include <bitset>
     16 #include <queue>
     17 #include <stack>
     18 #include <cmath>
     19 #include <ctime>
     20 #include <list>
     21 #include <set>
     22 #include <map>
     23 using namespace std;
     24 //using namespace __gnu_cxx;
     25 //define
     26 #define pii pair<int,int>
     27 #define mem(a,b) memset(a,b,sizeof(a))
     28 #define lson l,mid,rt<<1
     29 #define rson mid+1,r,rt<<1|1
     30 #define PI acos(-1.0)
     31 //typedef
     32 //typedef __int64 LL;
     33 //typedef unsigned __int64 ULL;
     34 //const
     35 const int N=500010;
     36 const int INF=0x3f3f3f3f;
     37 const int MOD=100000,STA=8000010;
     38 //const LL LNF=1LL<<60;
     39 const double EPS=1e-8;
     40 const double OO=1e15;
     41 const int dx[4]={-1,0,1,0};
     42 const int dy[4]={0,1,0,-1};
     43 const int day[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
     44 //Daily Use ...
     45 inline int sign(double x){return (x>EPS)-(x<-EPS);}
     46 template<class T> T gcd(T a,T b){return b?gcd(b,a%b):a;}
     47 template<class T> T lcm(T a,T b){return a/gcd(a,b)*b;}
     48 template<class T> inline T lcm(T a,T b,T d){return a/d*b;}
     49 template<class T> inline T Min(T a,T b){return a<b?a:b;}
     50 template<class T> inline T Max(T a,T b){return a>b?a:b;}
     51 template<class T> inline T Min(T a,T b,T c){return min(min(a, b),c);}
     52 template<class T> inline T Max(T a,T b,T c){return max(max(a, b),c);}
     53 template<class T> inline T Min(T a,T b,T c,T d){return min(min(a, b),min(c,d));}
     54 template<class T> inline T Max(T a,T b,T c,T d){return max(max(a, b),max(c,d));}
     55 //End
     56 
     57 #define Key_value ch[ch[root][1]][0]
     58 int pre[N],ch[N][2];  //分别表示父结点,键值,左右孩子(0为左孩子,1为右孩子),根结点,结点数量
     59 int sz[N],st[N];   //子树规模,内存池
     60 int root,tot,top;   //根节点,根节点数量,内存池容量
     61 //题目特定数目
     62 int val[N],sum[N],maxl[N],maxm[N],maxr[N],num[N];
     63 bool rev[N],flag[N];
     64 int n,m,posi,all;
     65 //debug部分copy from hh
     66 void Treaval(int x) {
     67     if(x) {
     68         Treaval(ch[x][0]);
     69         printf("结点%2d:val = %2d 左儿子 %2d 右儿子 %2d 父结点 %2d size = %2d maxm = %2d %2d %2d
    ",x,val[x],ch[x][0],ch[x][1],pre[x],sz[x],maxm[x],maxl[x],maxr[x]);
     70         Treaval(ch[x][1]);
     71     }
     72 }
     73 void debug() {printf("%d
    ",root);Treaval(root);}
     74 //以上Debug
     75 //新建一个结点
     76 void NewNode(int &x,int fa,int a)
     77 {
     78     if(top)x=st[top--];
     79     else x=++tot;
     80     sum[x]=val[x]=maxl[x]=maxm[x]=maxr[x]=a;
     81     pre[x]=fa;
     82     flag[x]=rev[x]=0;
     83     ch[x][0]=ch[x][1]=0;  //左右孩子为空
     84 }
     85 void Update_Same(int x,int v){
     86     if(!x) return;
     87     flag[x]=1;
     88     val[x]=v;
     89     sum[x]=sz[x]*v;
     90     maxl[x]=maxr[x]=maxm[x]=max(v,v*sz[x]);
     91 }
     92 void Update_Rev(int x){
     93     if(!x) return;
     94     swap(ch[x][0],ch[x][1]);
     95     swap(maxl[x],maxr[x]);  //翻转时左右区间也交换!!!!
     96     rev[x]^=1;
     97 }
     98 void Push_Up(int x)
     99 {
    100     int ls=ch[x][0],rs=ch[x][1];
    101     sz[x]=sz[ls]+sz[rs]+1;
    102     sum[x]=sum[ls]+sum[rs]+val[x];
    103     maxl[x]=Max(maxl[ls],sum[ls]+val[x]+Max(0,maxl[rs]));
    104     maxr[x]=Max(maxr[rs],sum[rs]+val[x]+Max(0,maxr[ls]));
    105     maxm[x]=Max(maxr[ls],0)+val[x]+Max(maxl[rs],0);
    106     maxm[x]=Max(maxm[x],maxm[ls],maxm[rs]);
    107 }
    108 
    109 void Push_Down(int x)
    110 {
    111     if(flag[x]){
    112         Update_Same(ch[x][0],val[x]);
    113         Update_Same(ch[x][1],val[x]);
    114         flag[x]=0;
    115     }
    116     if(rev[x]){
    117         Update_Rev(ch[x][0]);
    118         Update_Rev(ch[x][1]);
    119         rev[x]=0;
    120     }
    121 }
    122 //旋转,kind为1为右旋,kind为0为左旋
    123 void Rotate(int x,int kind)
    124 {
    125     int y=pre[x],z=pre[y];
    126     Push_Down(y);
    127     Push_Down(x);  //先把y的标记向下传递,再把x的标记往下传递
    128     //类似SBT,要把其中一个分支先给父节点
    129     ch[y][!kind]=ch[x][kind];
    130     pre[ch[x][kind]]=y;
    131     //如果父节点不是根结点,则要和父节点的父节点连接起来
    132     if(z)ch[z][ch[z][1]==y]=x;
    133     pre[x]=z;
    134     ch[x][kind]=y;
    135     pre[y]=x;
    136     Push_Up(y);  //维护y结点,不要维护x节点,x节点会再次Push_Down,最后维护一下x节点即可
    137 }
    138 //Splay调整,将根为r的子树调整为goal
    139 void Splay(int x,int goal)
    140 {
    141     int y,z,kind;
    142     while(pre[x]!=goal){
    143         //父节点即是目标位置,goal为0表示,父节点就是根结点
    144         y=pre[x];
    145         Push_Down(pre[y]);Push_Down(y);Push_Down(x);   //设计到反转操作,要先更新,然后在判断!!
    146         if(pre[y]==goal){
    147             Rotate(x,ch[y][0]==x);
    148         }
    149         else {
    150             kind=ch[pre[y]][0]==y;
    151             //两个方向不同,则先左旋再右旋
    152             if(ch[y][kind]==x){
    153                 Rotate(x,!kind);
    154                 Rotate(x,kind);
    155             }
    156             //两个方向相同,相同方向连续两次
    157             else {
    158                 Rotate(y,kind);
    159                 Rotate(x,kind);
    160             }
    161         }
    162     }
    163     //更新根结点
    164     Push_Up(x);
    165     if(goal==0)root=x;
    166 }
    167 
    168 void RotateTo(int k,int goal)
    169 {
    170     int x=root;
    171     Push_Down(x);
    172     while(sz[ch[x][0]]!=k){
    173         if(sz[ch[x][0]]>k)
    174             x=ch[x][0];
    175         else {
    176             k-=sz[ch[x][0]]+1;
    177             x=ch[x][1];
    178         }
    179         Push_Down(x);
    180     }
    181     Splay(x,goal);
    182 }
    183 //建树,中间结点先建立,然后分别对区间两端在左右子树建立
    184 void BuildTree(int &x,int l,int r,int fa)
    185 {
    186     if(l>r)return;
    187     int mid=(l+r)>>1;
    188     NewNode(x,fa,num[mid]);
    189     BuildTree(ch[x][0],l,mid-1,x);
    190     BuildTree(ch[x][1],mid+1,r,x);
    191     Push_Up(x);
    192 }
    193 
    194 void Init()
    195 {
    196     root=top=tot=0;
    197     ch[0][0]=ch[0][1]=sz[0]=pre[0]=0;
    198     val[0]=rev[0]=sum[0]=flag[0]=0;
    199     maxl[0]=maxm[0]=maxr[0]=-INF;
    200 
    201     for(int i=0;i<n;i++)
    202         scanf("%d",num+i);
    203     NewNode(root,0,0);
    204     NewNode(ch[root][1],root,0);
    205     BuildTree(Key_value,0,n-1,ch[root][1]);
    206     Push_Up(ch[root][1]);
    207     Push_Up(root);
    208 }
    209 
    210 void Insert()
    211 {
    212     RotateTo(posi,0);
    213     RotateTo(posi+1,root);
    214     BuildTree(Key_value,0,all-1,ch[root][1]);
    215     Push_Up(ch[root][1]);
    216     Push_Up(root);
    217 }
    218 
    219 void erase(int r){
    220     if(!r) return;
    221     st[++top]=r;
    222     erase(ch[r][0]);
    223     erase(ch[r][1]);
    224 }
    225 
    226 void Delete()
    227 {
    228     RotateTo(posi-1,0);
    229     RotateTo(posi+all,root);
    230     erase(Key_value);
    231     pre[Key_value]=0;
    232     Key_value=0;
    233     Push_Up(ch[root][1]);
    234     Push_Up(root);
    235 }
    236 
    237 void Update(int same)
    238 {
    239     RotateTo(posi-1,0);
    240     RotateTo(posi+all,root);
    241     int x=Key_value;
    242     if(x==0)return;
    243     flag[x]=1;
    244     val[x]=same;
    245     sum[x]=sz[x]*same;
    246     maxl[x]=maxm[x]=maxr[x]=Max(same,same*sz[x]);
    247     Push_Up(ch[root][1]);
    248     Push_Up(root);
    249 }
    250 
    251 void Reverse()
    252 {
    253     RotateTo(posi-1,0);
    254     RotateTo(posi+all,root);
    255     Update_Rev(Key_value);
    256  /* Push_Up维护  Wa...
    257     Push_Up(ch[root][1]);
    258     Push_Up(root);  */
    259 }
    260 
    261 int main()
    262 {
    263  //   freopen("in.txt","r",stdin);
    264     int i,j,k,a;
    265     char op[15];
    266     while(~scanf("%d%d",&n,&m))
    267     {
    268         Init();
    269         while(m--){
    270             scanf("%s",op);
    271             if(op[0]=='I'){
    272                 scanf("%d%d",&posi,&all);
    273                 for(i=0;i<all;i++)
    274                     scanf("%d",num+i);
    275                 Insert();
    276             }
    277             else if(op[0]=='D'){
    278                 scanf("%d%d",&posi,&all);
    279                 Delete();
    280             }
    281             else if(op[2]=='K'){
    282                 scanf("%d%d%d",&posi,&all,&a);
    283                 Update(a);
    284             }
    285             else if(op[0]=='R'){
    286                 scanf("%d%d",&posi,&all);
    287                 Reverse();
    288             }
    289             else if(op[0]=='G'){
    290                 scanf("%d%d",&posi,&all);
    291                 RotateTo(posi-1,0);
    292                 RotateTo(posi+all,root);
    293                 printf("%d
    ",sum[Key_value]);
    294             }
    295             else {
    296                 RotateTo(0,0);
    297                 RotateTo(sz[root]-1,root);
    298                 printf("%d
    ",maxm[Key_value]);
    299              /* 如果Push_Up维护,那么不需要上面的Totate()来维护  wa...
    300                 printf("%d
    ",maxm[root]);   */
    301             }
    302         }
    303     }
    304     return 0;
    305 }
  • 相关阅读:
    RapidJSON简介及使用(转)
    唯一ID生成算法剖析(转)
    OpenCV相机标定及距离估计(单目)(转)
    Linux 格式化分区 报错Could not stat --- No such file or directory 和 partprobe 命令
    Linux下chkconfig命令详解
    Linux设置开机服务自动启动
    CentOS 6.3下Samba服务器的安装与配置
    vmware 命令行启动虚拟机
    Linux自动修改IP脚本(手动编写)
    网络高清视频监控传输:如何减少带宽消耗?
  • 原文地址:https://www.cnblogs.com/zhsl/p/3227535.html
Copyright © 2020-2023  润新知