zoukankan      html  css  js  c++  java
  • Luogu P2572 [SCOI2010]序列操作(线段树)题解

    细节狂魔题

    题意:维护一个(01)序列,支持区间修改(全部变(0)(1)),区间取反,区间求和,区间求最长1序列

    做法

    显然应该用线段树维护。

    线段树需要维护:每段区间内(0)的数量,(1)的数量,最长(1)序列长度,最长(0)序列长度,从左端点开始的0/1序列长度,从右端点开始的(0/1)序列长度。很少吧

    注意!在标记下传时会有先后,赋值标记会覆盖翻转标记,而翻转标记会影响赋值标记

    为什么我觉得网上的题解都写的好复杂,其实区间修改与取反可以写在一起,求序列与求和也可以很方便地写出来。

    代码

    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    
    using namespace std;
    
    inline int read(){
        int x = 0, f = 1;
        char c = getchar();
        while(c < '0' || c > '9'){if(c == '-') f = -1; c = getchar();}
        while(c >= '0' && c <= '9'){x = x * 10 + c - '0'; c = getchar();}
        return x * f;
    }
    
    const int maxn = 1e5 + 10;
    int n,m;
    
    struct Seg_Tree{
        #define lc(x) x << 1
        #define rc(x) x << 1 | 1
        struct node{
            int c,c0,sum,lm,lm0,rm,rm0,tag,re;
        }t[maxn << 2];
        // c->num of continued 1; c0->num of continued 0;
        
        void update(int l, int r, int p){
            int mid = (l + r) >> 1;
            t[p].sum = t[rc(p)].sum + t[lc(p)].sum;
            t[p].c = max( max(t[lc(p)].c, t[rc(p)].c), t[lc(p)].rm + t[rc(p)].lm );
            t[p].c0 = max( max(t[lc(p)].c0, t[rc(p)].c0), t[lc(p)].rm0 + t[rc(p)].lm0 );
            
            if(t[lc(p)].lm == mid - l + 1) t[p].lm = mid - l + 1 + t[rc(p)].lm;
            else t[p].lm = t[lc(p)].lm;
            if(t[rc(p)].rm == r - mid) t[p].rm = r - mid + t[lc(p)].rm;
            else t[p].rm = t[rc(p)].rm;
            
            if(t[lc(p)].lm0 == mid - l + 1) t[p].lm0 = mid - l + 1 + t[rc(p)].lm0;
            else t[p].lm0 = t[lc(p)].lm0;
            if(t[rc(p)].rm0 == r - mid) t[p].rm0 = r - mid + t[lc(p)].rm0;
            else t[p].rm0 = t[rc(p)].rm0;
        }
        
        void build(int l, int r, int p){
            t[p].tag = 0;
            if(l == r){
                t[p].lm = t[p].rm = t[p].c = t[p].sum = read();
                t[p].lm0 = t[p].rm0 = t[p].c0 = t[p].c ^ 1;
                return;
            }
            int mid = (l + r) >> 1;
            build(l, mid, lc(p)); build(mid + 1, r, rc(p));
            update(l, r, p);
        }
        
        void f(int l, int r, int p, int typ){ //1 -> to 0; 2 -> to 1; 3 -> to ~
            if(typ == 1){
                t[p].sum = t[p].c = t[p].lm = t[p].rm = 0;
                t[p].c0 = t[p].lm0 = t[p].rm0 = r - l + 1;
                t[p].tag = 1; t[p].re = 0;
            }
            if(typ == 2){
                t[p].sum = t[p].c = t[p].lm = t[p].rm = r - l + 1;
                t[p].c0 = t[p].lm0 = t[p].rm0 = 0;
                t[p].tag = 2; t[p].re = 0;
            }
            if(typ == 3){
                t[p].sum = r - l + 1 - t[p].sum;
                swap(t[p].c0, t[p].c); swap(t[p].lm, t[p].lm0); swap(t[p].rm, t[p].rm0);
                if(t[p].tag == 1) t[p].tag = 2;
                else if(t[p].tag == 2) t[p].tag = 1;
                else t[p].re ^= 1;
            }
        }
        
        void downdate(int l, int r, int p){
            if(t[p].tag){
                int mid = (l + r) >> 1;
                f(l, mid, lc(p), t[p].tag);
                f(mid + 1, r, rc(p), t[p].tag);
                t[p].tag = 0; t[p].re = 0;
            }
            if(t[p].re){
                int mid = (l + r) >> 1;
                f(l, mid, lc(p), 3);
                f(mid + 1, r, rc(p), 3);
                t[p].re = 0;
            }
        }
        
        void change(int L, int R, int l, int r, int p, int typ){
            if(L <= l && R >= r){
                f(l, r, p, typ);
                return;
            }
            downdate(l, r, p);
            int mid = (l + r) >> 1;
            if(mid >= L) change(L, R, l, mid, lc(p), typ);
            if(mid < R) change(L, R, mid + 1, r, rc(p), typ);
            update(l, r, p);
        }
        
        int query(int L, int R, int l, int r, int p){ //get the num of continued 1
            if(L <= l && R >= r) return t[p].c;
            downdate(l, r, p);
            int mid = (l + r) >> 1; int ans = 0;
            if(mid >= L) ans = query(L, R, l, mid, lc(p));
            if(mid < R) ans = max(ans, query(L, R, mid + 1, r, rc(p)));
            if(mid >= L && mid < R) ans = max(ans, min(t[lc(p)].rm, mid - L + 1) + min(t[rc(p)].lm, R - mid));
            return ans;
        }
        
        int get_sum(int L, int R, int l, int r, int p){
            if(L <= l && R >= r) return t[p].sum;
            downdate(l, r, p);
            int mid = (l + r) >> 1; int ans = 0;
            if(mid >= L) ans += get_sum(L, R, l, mid, lc(p));
            if(mid < R) ans += get_sum(L, R, mid + 1, r, rc(p));
            return ans;
        }
    }tree;
    
    int main(){
        n = read(), m = read();
        tree.build(1, n, 1);
        for(int i = 1; i <= m; ++ i){
            int opt = read(), x = read(), y = read();
            x += 1, y += 1;
            if(opt == 0) tree.change(x, y, 1, n, 1, 1);
            if(opt == 1) tree.change(x, y, 1, n, 1, 2);
            if(opt == 2) tree.change(x, y, 1, n, 1, 3);
            if(opt == 3) printf("%d
    ", tree.get_sum(x, y, 1, n, 1));
            if(opt == 4) printf("%d
    ", tree.query(x, y, 1, n, 1));
        }
        return 0;
    }
    

    真是一点也不难写呢

    总结

    这类涉及带修区间求子序列的题基本上都是这种思路,考虑三种情况:答案序列在左边,答案序列在右边,答案序列横跨(mid),然后就可以很方(nan)便(tiao)地用线段树维护了。

    难想的主要是:(update)函数,(downdate)函数

  • 相关阅读:
    windows server 2012 如何开启 hyper-v 并创建虚拟机
    ABP框架系列之二十四:(Email-Sending-EF-电子邮件发送)
    ABP框架系列之二十三:(EF-MySql-Integration-EF-MySql-集成)
    ABP框架系列之二十二:(Dynamic-Web-API-动态WebApi)
    ABP框架系列之二十一:(Domain-Services-领域服务)
    ABP框架系列之二十:(Dependency-Injection-依赖注入)
    ABP框架系列之十九:(Debugging-调试)
    ABP框架系列之十八:(Data-Transfer-Objects-数据转换对象)
    ABP框架系列之十七:(Data-Filters-数据过滤)
    ABP框架系列之十六:(Dapper-Integration-Dapper集成)
  • 原文地址:https://www.cnblogs.com/whenc/p/13863185.html
Copyright © 2011-2022 走看看