PKU 3225 Help with Intervals

线段树经典入门题目;

这道题有几点不同的地方:

1. 维护的是集合,一个点或者一段区间都是一个集合,(2,3) 是一个集合,不能简单的用线段代替集合;

2. 用离散的 01 值表示集合,对集合的运算需要转化为 01 区间的异或与赋值操作;

3. 01 区间上进行异或和赋值,在 pushdown 的时候只能存在一个信息,一个线段要么被赋值,要么异或,不能既被赋值,同时进行异或(cover[]和xor[]不能同时有效);

4. 结果要求输出运算结果(集合),在查询时,要做的不是查询到左端点、右端点,而是把被赋值的区间全部标记下来(O(n)),然后一次扫描完成输出,这道题卡了很长时间就是这里没想清楚(既然用了线段树,为什么还要O(n)呢?其实是只需要一次O(n),何必用线段树呢?);

# include <stdio.h>

# include <string.h>



# define ls ((r)<<1)

# define rs ((r)<<1 | 1)

# define mid (((x)+(y))>>1)



# define maxn (65535 * 2)



char cc[maxn << 2], xx[maxn << 2],  hh[maxn+1];

/*************************************************************/

void XOR(int r)

{

    if (cc[r] != -1) cc[r] ^= 1;

    else xx[r] ^= 1;

}



void pushdown(int x, int y, int r)

{

    /* after pushdown cc[r]==-1 && xx[r]==0 */

    if (cc[r] != -1)

    {

        cc[ls] = cc[rs] = cc[r];

        xx[ls] = xx[rs] = 0;

        cc[r] = -1;

    }

    if (xx[r])

    {

        XOR(ls);

        XOR(rs);

        xx[r] = 0;

    }

}



void cover(int x, int y, int r, int s, int t, int val)

{

    if (s<=x && y<=t)

    {

        cc[r] = val;

        xx[r] = 0;

        return ;

    }

    pushdown(x, y, r);

    if (s <= mid) cover(x, mid, ls, s, t, val);

    if (mid+1<=t) cover(mid+1, y, rs, s, t, val);

}



void xxor(int x, int y, int r, int s, int t)

{

    if (s<=x && y<=t)

    {

        XOR(r);

        return ;

    }

    pushdown(x, y, r);

    if (s <= mid) xxor(x, mid, ls, s, t);

    if (mid+1<=t) xxor(mid+1, y, rs, s, t);

}



void query(int x, int y, int r)

{

    int i;

    if (cc[r]!= -1)

    {

        if (cc[r] == 1) for (i = x; i <= y; ++i) hh[i] = 1;

        return ;

    }

    // if (x == y) return ;

    pushdown(x, y, r);

    query(x, mid, ls);

    query(mid+1, y, rs);

}



/*************************************************************/

int main()

{

    freopen("data.in", "r", stdin);

    freopen("data.out", "w", stdout);

    

    int s, t, i, first = 1;

    char op, lp, rp;

    

    while (~scanf("%c %c%d,%d%c\n", &op, &lp, &s, &t, &rp))

    {

        s <<= 1, t <<= 1;

        if (lp == '(') ++s;

        if (rp == ')') --t;

        if (s > t || (s==t && (s&0x1)))

        {

            if (op == 'I' || op == 'C') cc[1] = 0, xx[1] = 0;

            continue;

        }

        switch(op)

        {

            case 'U': cover(0, maxn, 1, s, t, 1);break;

            case 'D': cover(0, maxn, 1, s, t, 0);break;

            case 'I': {

                          if (s>0)       cover(0, maxn, 1, 0, s-1, 0);

                          if (t<maxn) cover(0, maxn, 1, t+1, maxn, 0);

                          break;

                      }

             case 'C': {

                          if (s>0)    cover(0, maxn, 1, 0, s-1, 0);

                          if (t<maxn) cover(0, maxn, 1, t+1, maxn, 0);

                          xxor(0, maxn, 1, s, t);

                          break;

                      }          

             case 'S': xxor(0, maxn, 1, s, t);break;

        }

    }

    query(0, maxn, 1);

    for (i = 0; i <= maxn; ++i)

    {

        if (hh[i])

        {

            if (first) first = 0;

            else putchar(' ');

            printf("%c%d,", i&0x1 ? '(':'[', i>>1);

            while (hh[i]) ++i;

            printf("%d%c", i>>1, (i-1)&0x1 ? ')':']');

        }

    }

    if (first) puts("empty set");

    else putchar('\n');

    

    return 0;

} 

代码写的丑,但是刚好跑到了notonlysuccess的前面,谢谢他的线段树专辑

你可能感兴趣的:(with)