BZOJ 3226 [SDOI2008]校门外的区间

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3226

题意:

给定一个空集合S,维护五种集合与集合的操作,将最终得到的集合输出。

对于集合S和T,操作包括S=S∪T、S=S∩T、S=S?T、S=T?S、S=S?T。

每次的操作是给定一个T,并保证T表示的元素是一段连续的区间,可能开可能闭,区间端点均为整数。

最终输出的集合S要按升序输出每个连续的区间。

操作数≤70000,集合的数字∈[0,65535]。

题解:

考虑到区间的端点均为整数,若不考虑开闭问题则可以转化为整数点被覆盖的问题,而考虑开区间的话也只需要在任意两个整点之间加入一个点即可,不妨认为是X.5。再将坐标全部乘2则转化为整数点闭区间的问题。

考虑到集合内整点的个数并不多,坐标变换之后也只有131071个整点,不妨用一个01序列表示一个集合,序列第i位是1则表示集合中有整点i

现在考虑五种操作:(设坐标变换后T的区间为[l,r],坐标最大值为n)

- S=S∪T,相当于把S的[l,r]位置赋值为1。

- S=S∩T,相当于把S的[0,l)和(r,n]位置赋值为0。

- S=S?T,相当于把S的[l,r]位置赋值为0。

- S=T?S,相当于把S的[0,l)和(r,n]位置赋值为0,S的[l,r]位置的01互换。

- S=S?T,相当于把S的[l,r]位置的01互换。

考虑用线段树来维护这五个操作,需要对序列的一段染色,对序列的一段反色,输出每个连续的1区间(坐标需要变换回来)。

前两个操作需要O(logn)的复杂度,第三个操作只会做一次,复杂度O(n)即可。

所以线段树维护区间的颜色(0或1),打上反色的标记,注意两种标记下传的先后顺序即可,最终的操作只需要合并一下区间。

(听说线段树的做法比分块慢很多)

代码:

#include <cstdio>
#include <cstdlib>
const int n = 1 << 17;
int tot = 0, out[n][2];
struct SegmentTree
{
    SegmentTree *ch[2];
    int l, r, m;
    bool flag, Xor, color;
    SegmentTree(int l, int r) : l(l), r(r), m(l + r >> 1) { ch[0] = ch[1] = NULL; flag = 1; Xor = color = 0; }
    void update()
    {
        if(l == r) return;
        if(flag)
        {
            flag = 0;
            ch[0] -> flag = ch[1] -> flag = 1;
            ch[0] -> Xor = ch[1] -> Xor = 0;
            ch[0] -> color = ch[1] -> color = color;
        }
        if(Xor)
        {
            Xor = 0;
            ch[0] -> Xor ^= 1;
            ch[1] -> Xor ^= 1;
        }
    }
    void fill(const int s, const int t, const int col)
    {
        if(s <= l && r <= t)
        {
            flag = 1;
            Xor = 0;
            color = col;
            return;
        }
        update();
        if(s <= m) ch[0] -> fill(s, t, col);
        if(t > m) ch[1] -> fill(s, t, col);
    }
    void rev(const int s, const int t)
    {
        if(s <= l && r <= t)
        {
            Xor ^= 1;
            return;
        }
        update();
        if(s <= m) ch[0] -> rev(s, t);
        if(t > m) ch[1] -> rev(s, t);
    }
    void make_tree()
    {
        if(l == r) return;
        (ch[0] = new SegmentTree(l, m)) -> make_tree();
        (ch[1] = new SegmentTree(m + 1, r)) -> make_tree();
    }
    void pre()
    {
        if(flag)
        {
            color ^= Xor;
            if(color) { out[tot][0] = l; out[tot++][1] = r; }
            return;
        }
        update();
        ch[0] -> pre();
        ch[1] -> pre();
    }
    void del()
    {
        if(l == r)
            return;
        ch[0] -> del();
        delete ch[0];
        ch[1] -> del();
        delete ch[1];
    }
} *root;

int main()
{
    char type, flag_l, flag_r;
    int l, r;
    (root = new SegmentTree(0, n)) -> make_tree();
    while(scanf("%c %c%d,%d%c\n", &type, &flag_l, &l, &r, &flag_r) != EOF)
    {
        l <<= 1;
        r <<= 1;
        if(flag_l == ‘(‘) ++l;
        if(flag_r == ‘)‘) --r;
        switch(type)
        {
            case ‘U‘: root -> fill(l, r, 1); break;
            case ‘I‘: if(l) root -> fill(0, l - 1, 0); if(r < n) root -> fill(r + 1, n, 0); break;
            case ‘D‘: root -> fill(l, r, 0); break;
            case ‘C‘: if(l) root -> fill(0, l - 1, 0); if(r < n) root -> fill(r + 1, n, 0); root -> rev(l, r); break;
            case ‘S‘: root -> rev(l, r); break;
        }
    }
    root -> pre();
    if(!tot) printf("empty set\n");
    else
    {
        int i;
        for(i = 0; i < tot - 1; ++i)
            if(out[i][1] + 1 == out[i + 1][0]) out[i + 1][0] = out[i][0];
            else
            {
                if(out[i][0] & 1) putchar(‘(‘);
                else putchar(‘[‘);
                printf("%d,%d", out[i][0] >> 1, out[i][1] + 1 >> 1);
                if(out[i][1] & 1) putchar(‘)‘);
                else putchar(‘]‘);
                putchar(‘ ‘);
            }
        if(out[i][0] & 1) putchar(‘(‘);
        else putchar(‘[‘);
        printf("%d,%d", out[i][0] >> 1, out[i][1] + 1 >> 1);
        if(out[i][1] & 1) putchar(‘)‘);
        else putchar(‘]‘);
        putchar(‘ ‘);
        putchar(‘\n‘);
    }
    root -> del();
    delete root;
    return 0;
}
时间: 2024-08-05 14:01:38

BZOJ 3226 [SDOI2008]校门外的区间的相关文章

3226: [Sdoi2008]校门外的区间

链接 思路 bug漫天飞... 维护一颗线段树,支持区间赋值,和区间异或.因为会处理到一些方括号还是圆括号的问题,所以对于每一个下标都乘2,假设中间有一个.5即可,都变成了方括号,输出在处理一下. U  [l,r]赋值为1 I   [0,l-1],[r+1,n]赋值为0 D [l,r]区间涂0 C [0,l-1],[r+1,n]赋值为0,[l,r]区间异或 S [l,r]区间异或 bug列表:乘2后从0开始,因为0*2=0,0.5*2=1,zz的居然是从2开始的.. 读入的区间并不都是一位数..

[SDOI2008]校门外的区间

Description [SDOI2008]校门外的区间 ### Solution 两个整数之间再建一个点(如\(1\)和\(2\)之间再建一个\(1.5\)这个点),然后把开区间变成\(\pm1.5\)的闭区间 如\((1,5)\)变成\([1.5,4.5]\) \(U\) 区间涂色 \(I\) 两侧区间涂\(0\) \(D\) 区间涂\(0\) \(C\) 两侧涂\(0\),中间取反 \(S\) 区间取反 Code 原文地址:https://www.cnblogs.com/Agakiss/p

BZOJ3226[Sdoi2008]校门外的区间 题解

题目大意: 有5种运算维护集合S(S初始为空)并最终输出S. 5种运算如下: U T S∪T I T S∩T D T S-T C T T-S S T S⊕T 基本集合运算如下: A∪B      {x : xÎA or xÎB} A∩B      {x : xÎA and xÎB} A-B      {x : xÎA and xÏB} A⊕B      (A-B)∪(B-A) 思路: 每个数之间加入一个数,就像这样2 2.5 3 3.5 4 [2,3) -> [2,2.5] (3,4] ->

bzoj3226: [Sdoi2008]校门外的区间 线段树

题比较有趣,输入输出比较麻烦. 每个点拆成两个,线段树维护.(这题难点真的在输入输出) #include<bits/stdc++.h> #define N (1<<17) #define M (l+r>>1) #define P (k<<1) #define S (k<<1|1) #define K l,r,k #define L l,M,P #define R M+1,r,S #define Z int l=0,int r=N,int k=1

【分块】bzoj3226 [Sdoi2008]校门外的区间

题解见 : http://blog.csdn.net/iamzky/article/details/41088151 ORZ ZKY 2个懒标记:是否翻转,覆盖成了什么. 怎么处理一个块上有两个标记的情况呢? 若该块原来没有任何标记,或要打的标记和原本的标记种类相同,则直接打上标记: 若已有翻转标记,再覆盖时则先清除翻转标记,再打上覆盖标记: 若已有覆盖标记,再翻转时,则直接将覆盖标记取反. So 某个块上同时只会有1个标记. P.S.分块此题挺快的…… 1 #include<cstdio>

[bzoj 3226]校门外的区间

题意 输出最后的集合   题解 校门外的树会做吧 区间知道是什么东西吧 校门外的区间会做了吧 昨天做个大线段树没做出来,今天做个小线段树压压惊 py一下输入数据,然后操作变成: U 区间涂1 I 两侧区间涂0 D 区间涂0 C 两侧涂0,中间取反 S 区间取反 #include<map> #include<stack> #include<queue> #include<cstdio> #include<string> #include<ve

TyvjOJ题目 P1473 校门外的树3(线段树区间染色种类数不覆盖)

P1473 校门外的树3 时间: 1000ms / 空间: 131072KiB / Java类名: Main 描述 校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的-- 如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作: K=1,读入l,r表示在l~r之间种上的一种树 K=2,读入l,r表示询问l~r之间能见到多少种树 (l,r>0) 输入格式 第一行n,m表示道路总长为n,共有m个操作 接下来m行为m个操作 输出格式 对于每个k=

vijos P1448 校门外的树

描述 校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的……如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作:K=1,K=1,读入l.r表示在区间[l,r]中种上一种树,每次操作种的树的种类都不同K=2,读入l,r表示询问l~r之间能见到多少种树(l,r>0) 输入格式 第一行n,m表示道路总长为n,共有m个操作接下来m行为m个操作 输出格式 对于每个k=2输出一个答案 提示 范围:20%的数据保证,n,m<=10060%的数据保证

C语言 &#183; 校门外的树

算法提高 校门外的树 时间限制:1.0s   内存限制:256.0MB 问题描述 某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米.我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置:数轴上的每个整数点,即0,1,2,--,L,都种有一棵树. 由于马路上有一些区域要用来建地铁.这些区域用它们在数轴上的起始点和终止点表示.已知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分.现在要把这些区域中的树(包括区域端点处的两棵树)移走.你的任务是计算