HDU-3397 Sequence operation

本文介绍了一种基于线段树的数据结构解决区间修改和查询的问题,包括区间覆盖、区间反转及最长连续子串长度等复杂操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Sequence operation

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Problem Description
lxhgww got a sequence contains n characters which are all '0's or '1's.
We have five operations here:
Change operations:
0 a b change all characters into '0's in [a , b]
1 a b change all characters into '1's in [a , b]
2 a b change all '0's into '1's and change all '1's into '0's in [a, b]
Output operations:
3 a b output the number of '1's in [a, b]
4 a b output the length of the longest continuous '1' string in [a , b]
 

Input
T(T<=10) in the first line is the case number.
Each case has two integers in the first line: n and m (1 <= n , m <= 100000).
The next line contains n characters, '0' or '1' separated by spaces.
Then m lines are the operations:
op a b: 0 <= op <= 4 , 0 <= a <= b < n.
 

Output
For each output operation , output the result.
 

Sample Input
  
  
1 10 10 0 0 0 1 1 0 1 0 1 1 1 0 2 3 0 5 2 2 2 4 0 4 0 3 6 2 3 7 4 2 8 1 0 5 0 5 6 3 3 9
 

Sample Output
  
  
5 2 6 5
 
————————————————————丢人的分割线————————————————————
前言:一道题做了这么多天。是丢人。物理补考也挂了,要交钱重修了。
思路:这道题是把几道线段树区间操作的题目合起来了。一个是POJ-3225 Help with Intervals一个是POJ-3667 Hotel一个是HDU-1698 Just a Hook。最基本的懒惰标记求区间和,成段替换以及区间合并。三种操作都要实现。代码能力渣的话要做很久才能做对。真是一道综合题。
对build、up、down操作都是只要细心就能写好的,不难。但是难点在于0/1互换以及询问最长连续1的长度。
之前的Help with Intervals的区间翻转直接移植过来是会失败的。因为那道题并没有数据域。因此在进行翻转操作的时候没有更新数据域,仅仅只是翻转了标记。
但是这道题,一旦进行了翻转操作,就必须更新当前结点的数据域,否则对翻转来说,Down操作根本没用!
除此以外,对于最值的查询,也不像Hotel那样,那时候因为查询的是特定长度,自左向右,所以只要不是单纯在左儿子或者右儿子当中,就可以直接地返回查询值了。
但是这次,就要三次:
如果单纯左儿子,递归进入左儿子,并返回值。
如果单纯右儿子,递归进入右儿子,并返回值。
否则的话,再次递归进入左儿子,递归进入右儿子,之后再计算如果在合并之后的区间内的话,既在查询区间内又在合并后的连续区间内的值,以上三个值取最值并返回。
代码如下:
/*
ID: j.sure.1
PROG:
LANG: C++
*/
/****************************************/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <string>
#include <climits>
#include <iostream>
#define INF 0x3f3f3f3f
using namespace std;
/****************************************/
const int N = 1e5+5;
int n, m, u, x[N];
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
#define MID int m = (l+r)>>1
#define node int l, int r, int rt
#define quote Node &ll = t[rt<<1], &rr = t[rt<<1|1], &tt = t[rt];
#define Swap(a, b) u = a; a = b; b = u;
struct Node {
    int maxi[2], left[2], right[2];
    int    sum;
    int cov; bool opp;
}t[N<<2];

void EX(int rt, int len)
{
    Swap(t[rt].maxi[0], t[rt].maxi[1]);
    Swap(t[rt].left[0], t[rt].left[1]);
    Swap(t[rt].right[0], t[rt].right[1]);
    t[rt].sum = len - t[rt].sum;//0、1个数互换
}

void toop(int rt, int len)
{
    if(t[rt].cov != -1) {
        t[rt].cov = !t[rt].cov;
        t[rt].sum = len * (t[rt].cov == 1);
        for(int i = 0; i < 2; i++) {
            t[rt].left[i] = t[rt].right[i] = t[rt].maxi[i] = (i == t[rt].cov)*len;
        }
    }
    else { 
        t[rt].opp = !t[rt].opp;
        EX(rt, len);
    }
}//难点,这里不只是翻转了标记,还需要更新数据域

void Down(int rt, int len)
{
    quote;
    if(tt.cov != -1) {
        ll.cov = rr.cov = tt.cov;//下放标记
        ll.opp = rr.opp = 0;//翻转无效
        ll.sum = (tt.cov == 1) * (len+1>>1);
        rr.sum = (tt.cov == 1) * (len>>1);//根据标记更新左右儿子的和
        for(int i = 0; i < 2; i++) {
            ll.left[i] = ll.right[i] = ll.maxi[i] = (tt.cov == i)*(len+1>>1);
            rr.left[i] = rr.right[i] = rr.maxi[i] = (tt.cov == i)*(len >> 1);
        }//更新最值!
        tt.cov = -1;
    }
    if(tt.opp) {//左右儿子根据翻转标记进行更新
        toop(rt<<1, len+1>>1);
        toop(rt<<1|1, len>>1);
        tt.opp = 0;
    }
}

void Up(int rt, int len)
{
    quote;
    tt.sum = ll.sum + rr.sum;
    for(int i = 0; i < 2; i++) {
        tt.left[i] = ll.left[i];
        tt.right[i] = rr.right[i];
        if(tt.left[i] == len+1>>1) tt.left[i] += rr.left[i];
        if(tt.right[i] == len>>1) tt.right[i] += ll.right[i];
        tt.maxi[i] = max(ll.right[i] + rr.left[i], max(ll.maxi[i], rr.maxi[i]));
    }
}

void update(int c, int L, int R, node)
{
    quote;
    if(L <= l && r <= R) {
        if(c < 2) {
            tt.cov = c; tt.opp = 0;
            tt.sum = c ? r-l+1 : 0;
            for(int i = 0; i < 2; i++) {
                tt.left[i] = tt.right[i] = tt.maxi[i] = (c == i)*(r-l+1);
            }
        }
        else toop(rt, r-l+1);
        return ;
    }
    Down(rt, r-l+1);
    MID;
    if(L <= m) update(c, L, R, lson);
    if(m < R) update(c, L, R, rson);
    Up(rt, r-l+1);
}

int Qmax(int L, int R, node)
{
    quote;
    if(L <= l && r <= R) {
        return tt.maxi[1];
    }
    Down(rt, r-l+1);
    MID;
    int retl, retr;
    if(R <= m) return Qmax(L, R, lson);//单纯在左儿子当中
    if(m < L) return Qmax(L, R, rson);//单纯在右儿子当中
    retl = Qmax(L, R, lson);
    retr = Qmax(L, R, rson);
    int ret = max(retl, retr);
    int ans = min(m-L+1, ll.right[1]) + min(R-m, rr.left[1]);//在合并之后的区间内
    return max(ret, ans);//三段区间取最值
}//难点

int Qsum(int L, int R, node)
{
    quote;
    if(L <= l && r <= R) {
        return tt.sum;
    }
    Down(rt, r-l+1);
    MID;
    int ret = 0;
    if(L <= m) ret += Qsum(L, R, lson);
    if(m < R) ret += Qsum(L, R, rson);
    return ret;
}

void build(node)
{
    t[rt].cov = -1; t[rt].opp = 0;
    if(l == r) {
        scanf("%d", &t[rt].sum);
        t[rt].cov = t[rt].sum;
        for(int i = 0; i < 2; i++)
            t[rt].maxi[i] = t[rt].left[i] = t[rt].right[i] = (t[rt].cov == i);
        return ;
    }
    MID;
    build(lson); build(rson);
    Up(rt, r-l+1);
}

int main()
{
#ifdef J_Sure
    freopen("000.in", "r", stdin);
    freopen("999.out", "w", stdout);
#endif
    int T;
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d", &n, &m);
        build(0, n-1, 1);
        while(m--) {
            int op, A, B;
            scanf("%d%d%d", &op, &A, &B);
            if(op < 3) {
                update(op, A, B, 0, n-1, 1);
            }
            else {
                if(op == 3)
                    printf("%d\n", Qsum(A, B, 0, n-1, 1));
                else
                    printf("%d\n", Qmax(A, B, 0, n-1, 1));
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值