• [SCOI2010]序列操作 线段树


    ~~~题面~~~

    题解:

      在考场上打的这道题,出人意料的很快就打完了?!

      直接用线段树,维护几个东西:

      1,lazy标记 : 表示区间赋值

      2,mark标记:表示区间翻转

      3,l1:前缀最长连续的1的子段长度

      4,l0:前缀最长连续的0的子段长度

      5,m0:区间内最长的全为0的子段的长度

      6,r0:后缀最长连续的0的子段长度

      7,r1:后缀最长连续的1的子段长度

      8,m1:区间内最长的全为1的子段的长度

      9,sum :区间和

      维护起来比较繁琐,细节较多,但是都不难,是可以自己想出来的。

      这里提一个可以忽略标记处理顺序的小技巧:

      因为lazy标记是可以覆盖mark标记的,因此一个节点在得到lazy标记时,清空mark标记。

      因为mark标记可以看做是直接对lazy标记进行翻转,因此如果一个节点已经有lazy标记,那么在打上mark标记时,可以选择不得到mark标记,而是直接对lazy标记进行修改。

      因此不论是什么情况,mark标记和lazy标记都不会同时存在,也就不会有处理的先后顺序问题了。

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define R register int
      4 #define AC 101000
      5 #define ac 800000
      6 #define LL long long
      7 int n, m, ans;
      8 int s[AC];
      9 int sum[ac], lazy[ac], l1[ac], r1[ac], l0[ac], r0[ac], m1[ac], m0[ac], l[ac], r[ac];
     10 bool mark[ac];
     11 
     12 inline int read()
     13 {
     14     int x = 0;char c = getchar();
     15     while(c > '9' || c < '0') c = getchar();
     16     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
     17     return x; 
     18 }
     19 
     20 inline void upmin(int &a, int b)
     21 {
     22     if(b < a) a = b;
     23 }
     24 
     25 inline void upmax(int &a, int b)
     26 {
     27     if(b > a) a = b;
     28 }
     29 
     30 inline int Max(int a, int b)
     31 {
     32     if(a > b) return a;
     33     else return b; 
     34 }
     35 
     36 void update(int x)//更新信息
     37 {
     38     int ll = x * 2, rr = x * 2 + 1;
     39     m1[x] = Max(m1[ll], m1[rr]);
     40     upmax(m1[x], r1[ll] + l1[rr]);
     41     m0[x] = Max(m0[ll], m0[rr]);
     42     upmax(m0[x], r0[ll] + l0[rr]);
     43     if(sum[rr] == r[rr] - l[rr] + 1) r1[x] = sum[rr] + r1[ll];
     44     else r1[x] = r1[rr];
     45     if(!sum[rr]) r0[x] = r[rr] - l[rr] + 1 + r0[ll];
     46     else r0[x] = r0[rr];
     47     if(sum[ll] == r[ll] - l[ll] + 1) l1[x] = sum[ll] + l1[rr];
     48     else l1[x] = l1[ll];
     49     if(!sum[ll]) l0[x] = r[ll] - l[ll] + 1 + l0[rr];
     50     else l0[x] = l0[ll];
     51     sum[x] = sum[ll] + sum[rr];
     52 }
     53 
     54 void getlazy(int x, int go)
     55 {
     56     if(go == 1)
     57     {
     58         lazy[x] = 1, sum[x] = 0;
     59         l1[x] = r1[x] = m1[x] = 0;
     60         l0[x] = r0[x] = m0[x] = r[x] - l[x] + 1;        
     61     }
     62     else if(go == 2)
     63     {
     64         lazy[x] = 2;
     65         sum[x] = r[x] - l[x] + 1;
     66         l1[x] = r1[x] = m1[x] = sum[x];
     67         l0[x] = r0[x] = m0[x] = 0;
     68     }
     69     mark[x] = 0;//区间赋值可以抵消区间反转
     70 }
     71 
     72 void getmark(int x)
     73 {
     74     mark[x] ^= 1;//翻转翻转标记
     75     if(lazy[x]) getlazy(x, lazy[x] % 2 + 1);
     76     else
     77     {
     78         sum[x] = r[x] - l[x] + 1 - sum[x];
     79         swap(l1[x], l0[x]), swap(r1[x], r0[x]);
     80         swap(m1[x], m0[x]);
     81     }
     82 }
     83 
     84 void pushdown(int x)//下传标记
     85 {
     86     if(lazy[x])
     87     {    
     88         int ll = x * 2, rr = ll + 1;
     89         if(lazy[x] == 1)//change to 0
     90             getlazy(ll, 1), getlazy(rr, 1);
     91         else//change to 1
     92             getlazy(ll, 2), getlazy(rr, 2);
     93         lazy[x] = 0;
     94     }
     95     if(mark[x])
     96     {
     97         int ll = x * 2, rr = ll + 1;
     98         getmark(ll), getmark(rr);
     99         mark[x] = 0;
    100     }
    101 }
    102 
    103 void change(int x, int ll, int rr, int go)//区间修改
    104 {
    105     pushdown(x);
    106     if(l[x] == ll && r[x] == rr)
    107     {
    108         if(go <= 2) getlazy(x, go);
    109         else getmark(x);
    110         return;
    111     }
    112     int mid = (l[x] + r[x]) >> 1;
    113     if(rr <= mid) change(x * 2, ll, rr, go);
    114     else if(ll > mid) change(x * 2 + 1, ll, rr, go);
    115     else 
    116     {
    117         change(x * 2, ll, mid, go);
    118         change(x * 2 + 1, mid + 1, rr, go);
    119     }
    120     update(x);
    121 }
    122 
    123 void getsum(int x, int ll, int rr)
    124 {
    125     pushdown(x);
    126     if(l[x] == ll && r[x] == rr) 
    127     {
    128         ans += sum[x];
    129         return ;
    130     }
    131     int mid = (l[x] + r[x]) >> 1;
    132     if(rr <= mid) getsum(x * 2, ll, rr);
    133     else if(ll > mid) getsum(x * 2 + 1, ll, rr);
    134     else
    135     {
    136         getsum(x * 2, ll, mid);
    137         getsum(x * 2 + 1, mid + 1, rr);
    138     }
    139 }
    140 
    141 void find(int x, int ll, int rr)//询问连续1的长度
    142 {
    143     pushdown(x);
    144     if(l[x] == ll && r[x] == rr)
    145     {
    146         upmax(ans, m1[x]);
    147         return ;
    148     }
    149     int mid = (l[x] + r[x]) >> 1;
    150     if(rr <= mid) find(x * 2, ll, rr);
    151     else if(ll > mid) find(x * 2 + 1, ll, rr);
    152     else
    153     {
    154         int tmp = min(mid - ll + 1, r1[x * 2]) + min(rr - mid, l1[x * 2 + 1]);
    155         find(x * 2, ll, mid);
    156         find(x * 2 + 1, mid + 1, rr);
    157         upmax(ans, tmp);
    158     }
    159 }
    160 
    161 void build(int x, int ll ,int rr)
    162 {
    163     l[x] = ll, r[x] = rr;
    164     if(l[x] == r[x]) 
    165     {
    166         sum[x] = l1[x] = r1[x] = m1[x] = s[l[x]];
    167         if(!sum[x]) l0[x] = r0[x] = m0[x] = 1;
    168         return ;
    169     }
    170     int mid = (ll + rr) >> 1;
    171     build(x * 2, ll, mid);
    172     build(x * 2 + 1, mid + 1, rr);
    173     update(x);
    174 }
    175 
    176 void pre()
    177 {
    178     n = read(), m = read();
    179     for(R i = 1; i <= n; i ++) s[i] = read();
    180 }
    181 
    182 void work()
    183 {
    184     int opt, a, b;
    185     for(R i = 1; i <= m; i ++)
    186     {
    187     //    printf("!!!%d
    ", i);
    188         opt = read(), a = read() + 1, b = read() + 1;
    189         if(!opt) change(1, a, b, 1);
    190         else if(opt == 1) change(1, a, b, 2);
    191         else if(opt == 2) change(1, a, b, 3);
    192         else if(opt == 3)
    193         {
    194             ans = 0;
    195             getsum(1, a, b);
    196             printf("%d
    ", ans);
    197         }
    198         else if(opt == 4)
    199         {
    200             ans = 0;
    201             find(1, a, b);
    202             printf("%d
    ", ans);
    203         }
    204     }
    205 }
    206 
    207 int main()
    208 {
    209     //freopen("operation.in", "r", stdin);
    210 //    freopen("operation.out", "w", stdout);
    211     pre();
    212     build(1, 1, n);
    213     work();
    214     //fclose(stdin);
    215     //fclose(stdout);
    216     return 0;
    217 }
    View Code
  • 相关阅读:
    012 spring retry重试原理的解析
    011 @Retryable的使用
    010 @ControllerAdvice
    009 SpringBoot+Swagger的使用
    008 @Import作用
    007 SpringBoot的@EnableAutoConfiguration注解
    001 品牌管理案例
    000 vue各种基本指令
    013 JS
    002 docker基本的命令
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9614689.html
Copyright © 2020-2023  润新知