11402 - Ahoy, Pirates!(线段树区间更新(标记重叠的处理))

题目链接:点击打开链接

题意:有3种区间操作, 将某个区间全部变成1; 将某个区间全部变成0;将某个区间的1变成0, 0变成1。

思路:前两个操作就是最基本的区间更新, 用到懒惰标记, 然而第3个操作却有些麻烦, 如果仅仅更新当前这个结点对应的大区间, 那么它所包含的小区间再次更新时就会发生错误, 错误的原因是因为标记的重叠和碰撞。  显然 , 这就是很典型的一个问题, 处理标记碰撞的问题。

问题的核心是解决碰撞, 使得每个点每个时刻至多只有一个标记。

那么怎么办呢?我们可以在两个标记发生碰撞的时候来看看会出现什么等价的情况:

当我们要更新一个结点o时:

如果我们要进行区间更新, 那么直接覆盖而不用管原来的标记。 但是如果是区间反转的时候:

如果该结点已经有一个标记v

那么:

当v == 1, 说明该区间上一次有一次全部变成1的操作, 等价于这次全部变成0, 所以懒惰标记最终变成0.

当v == 0,同理, 懒惰标记最终变成1.

当v == 2,说明该区间上一次有一个全部反转的操作还没有实现, 等价于这次不反转, 所以懒惰标记最终变成-1

当v == -1,说明该区间上一次没有操作, 等价于这次需要反转, 懒惰标记最终为2

其实就是一个等价转换而已, 当碰撞多了还将涉及一个处理的顺寻问题。

还有一点, 如果你是在更新结点v时就更新了sum[v], 那么在等价转换的时候还涉及到sum[v]的更新问题, 不要想错了。

细节参见代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int mod = 1000000000 + 7;
const int INF = 1000000000;
const int maxn = 1024010;
int T,n,m,q,l,r,kase=0,sum[maxn<<2],setv[maxn<<2],a[maxn],cnt = 0;
void PushUp(int o) {
    sum[o] = sum[o<<1] + sum[o<<1|1];
}
void pushdown(int l, int r, int o) {
    if(setv[o] != -1) {
        int m = (l + r) >> 1;
        if(setv[o] == 1) {
            sum[o<<1] = (m - l + 1);
            sum[o<<1|1] = (r - m);
            setv[o<<1] = setv[o<<1|1] = setv[o];
        }
        else if(setv[o] == 0) {
            sum[o<<1] = 0;
            sum[o<<1|1] = 0;
            setv[o<<1] = setv[o<<1|1] = setv[o];
        }
        else {
            if(setv[o<<1] == 1) {
                setv[o<<1] = 0;
                sum[o<<1] = 0;
            }
            else if(setv[o<<1] == 0) {
                setv[o<<1] = 1;
                sum[o<<1] = (m - l + 1);
            }
            else if(setv[o<<1] == 2) setv[o<<1] = -1, sum[o<<1] = m - l + 1 - sum[o<<1];
            else {
                setv[o<<1] = 2;
                sum[o<<1] = m - l + 1 - sum[o<<1];
            }

            if(setv[o<<1|1] == 1) {
                setv[o<<1|1] = 0;
                sum[o<<1|1] = 0;
            }
            else if(setv[o<<1|1] == 0) {
                setv[o<<1|1] = 1;
                sum[o<<1|1] = (r - m);
            }
            else if(setv[o<<1|1] == 2) setv[o<<1|1] = -1, sum[o<<1|1] = r - m - sum[o<<1|1];
            else {
                setv[o<<1|1] = 2;
                sum[o<<1|1] = r - m - sum[o<<1|1];
            }
        }
        setv[o] = -1;
    }
}
void build(int l, int r, int o) {
    int m = (l + r) >> 1;
    setv[o] = -1;
    if(l == r) {
        ++cnt;
        if(a[cnt]) sum[o] = 1;
        else sum[o] = 0;
        return ;
    }
    build(l, m, o<<1);
    build(m+1, r, o<<1|1);
    PushUp(o);
}
void update(int L, int R, int v, int l, int r, int o) {
    int m = (l + r) >> 1;
    if(L <= l && r <= R) {
        if(v == 1) {
            sum[o] = (r - l + 1);
            setv[o] = 1;
        }
        else if(v == 0) {
            sum[o] = 0;
            setv[o] = 0;
        }
        else {
            if(setv[o] == 1) {
                setv[o] = 0;
                sum[o] = 0;
            }
            else if(setv[o] == 0) {
                setv[o] = 1;
                sum[o] = r - l + 1;
            }
            else if(setv[o] == -1) {
                setv[o] = 2;
                sum[o] = r - l + 1 - sum[o];
            }
            else setv[o] = -1, sum[o] = r - l + 1 - sum[o];
        }
        return ;
    }
    pushdown(l, r, o);
    if(L <= m) update(L, R, v, l, m, o<<1);
    if(m < R) update(L, R, v, m+1, r, o<<1|1);
    PushUp(o);
}
int query(int L, int R, int l, int r, int o) {
    int m = (l + r) >> 1;
    if(L <= l && r <= R) {
        return sum[o];
    }
    pushdown(l, r, o);
    int ans = 0;
    if(L <= m) ans += query(L, R, l, m, o<<1);
    if(m < R) ans += query(L, R, m+1, r, o<<1|1);
    PushUp(o);
    return ans;
}
char s[100];
int main() {
    scanf("%d",&T);
    while(T--) {
        scanf("%d",&m);
        int n = 0;
        for(int i=0;i<m;i++) {
            scanf("%d%s",&cnt,s);
            int len = strlen(s);
            while(cnt--) {
                for(int j=0;j<len;j++) {
                    a[++n] = s[j] - '0';
                }
            }
        }
        cnt = 0;
        build(1, n, 1);
        scanf("%d",&q);
        printf("Case %d:\n",++kase);
        int cc = 0;
        while(q--) {
            scanf("%s%d%d",s,&l,&r);
            l++; r++;
            if(s[0] == 'F') update(l, r, 1, 1, n, 1);
            else if(s[0] == 'E') update(l, r, 0, 1, n, 1);
            else if(s[0] == 'I') update(l, r, 2, 1, n, 1);
            else printf("Q%d: %d\n",++cc, query(l, r, 1, n, 1));
        }
    }
    return 0;
}
时间: 2024-08-04 19:53:19

11402 - Ahoy, Pirates!(线段树区间更新(标记重叠的处理))的相关文章

UVA - 11402 Ahoy, Pirates! (线段树)

In the ancient pirate ages, the Pirate Land was divided into two teams ofpirates, namely, the Buccaneer and the Barbary pirates.Each pirate's team was not fixed, sometimes the opponent pirates attacked andhe was taken away to the other pirate team. A

hihocoder 1080 线段树(区间更新)

题目链接:http://hihocoder.com/problemset/problem/1080 , 两种操作的线段树(区间更新). 这道题前一段时间一直卡着我,当时也是基础不扎实做不出来,今天又想了想其实还是比较简单的,也只能怪自己太弱了. 这道题坑就坑在是有两个操作:set和add,所以lazy标记数组就需要两个,但是有一点要考虑的是一个区间上set与add的先后关系——如果对于一个区间set和add标记同时存在,那么应该如果处理:一种情况set在add之前,那么就按照正常顺序来就可以了:

线段树区间更新

区间更新也可以分割成若干个子区间, 每层的结点至多选取 2 个,时间复杂度 O(logn). 懒惰(Lazy)标记 懒惰标记,也可称为延迟标记.一个区间可以转化为若干个结点,每个结点设一个标记,记录这个结点被进行了某种修改操作(这种修改操作会影响其子结点). 也就是说,仅修改到这些结点,暂不修改其子结点:而后决定访问其子节点时,再下传懒惰 (Lazy) 标记,并消除原来的标记. 优点在于,不用将区间的所有值暴力更新,大大提高效率. 在区间修改的一类问题中,我们可以设一个 delta 域,表示该节

POJ 3468 A Simple Problem with Integers(线段树区间更新)

题目地址:POJ 3468 打了个篮球回来果然神经有点冲动..无脑的狂交了8次WA..居然是更新的时候把r-l写成了l-r... 这题就是区间更新裸题.区间更新就是加一个lazy标记,延迟标记,只有向下查询的时候才将lazy标记向下更新.其他的均按线段树的来就行. 代码如下: #include <iostream> #include <cstdio> #include <cstring> #include <math.h> #include <stac

hihoCoder 1080 : 更为复杂的买卖房屋姿势 线段树区间更新

#1080 : 更为复杂的买卖房屋姿势 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho都是游戏迷,“模拟都市”是他们非常喜欢的一个游戏,在这个游戏里面他们可以化身上帝模式,买卖房产. 在这个游戏里,会不断的发生如下两种事件:一种是房屋自发的涨价或者降价,而另一种是政府有关部门针对房价的硬性调控.房价的变化自然影响到小Hi和小Ho的决策,所以他们希望能够知道任意时刻某个街道中所有房屋的房价总和是多少——但是很不幸的,游戏本身并不提供这样的计算.不过这难

Uva 1232 - SKYLINE ( 线段树 + 区间更新 )

Uva 1232 SKYLINE (线段树 + 区间更新) 题意: 按照顺序在地面上建造放在,每个房子的高度为h,操作 l r h 表示 在(l,r] 区间建立一个高度为h的房子.统计每次建立完房子之后的overlap值之和 overlap值表示[ 修完一座房子之后,统计它在多长的部分是最高的(可以和其他房子并列高) ]如样例图,按照灰.黒.白的顺序建立房子 ans = (11-5) + (5-1) + (5-3) + (13-11) = 6 + 4 + 4 = 14 分析: 一开始一直想不明白

线段树 区间更新

poj3468 A Simple Problem with Integers ( m - ( m >> 1 ) )这里跪了几发.. - 的优先级大于 >> 1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdio> 5 #include<string> 6 #include<queue> 7 #include

HDU 1698 Just a Hook (线段树,区间更新)

Just a Hook Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 17214    Accepted Submission(s): 8600 Problem Description In the game of DotA, Pudge’s meat hook is actually the most horrible thing f

ZOJ 1610 Count the Colors (线段树区间更新)

题目链接 题意 : 一根木棍,长8000,然后分别在不同的区间涂上不同的颜色,问你最后能够看到多少颜色,然后每个颜色有多少段,颜色大小从头到尾输出. 思路 :线段树区间更新一下,然后标记一下,最后从头输出. //ZOJ 1610 #include <cstdio> #include <cstring> #include <iostream> using namespace std ; int p[8010*4],lz[8010*4] ,hashh[8010*4],has

线段树 + 区间更新 + 模板 ---- poj 3468

A Simple Problem with Integers Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 59798   Accepted: 18237 Case Time Limit: 2000MS Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of