#6 RE 求调
查看原帖
#6 RE 求调
1107998
Flaw_Owl楼主2024/3/27 08:27

调了一下午,百思不得其解。讨论区数年前有相关的讨论但已经无法查看了。希望有大佬帮忙QAQ

#include <iostream>
#include <cctype>

using namespace std;

const int maxN = 132010;
const int N = 132010;

int tree[maxN << 2];    // 线段树
int ans[maxN << 2];     // 存储最后的集合答案,为 1 表示存在
int tag_set[maxN << 2]; // 赋值的lazy-tag,-1表示不存在
int tag_rev[maxN << 2]; // 翻转的lazy-tag
int ls(int p) { return p << 1; }
int rs(int p) { return p << 1 | 1; }

/// @brief 向上传递区间和,如果区间和等于区间长度,说明这个区间内所有的点都为 1
/// @param p 根节点编号
void push_up(int p)
{
    tree[p] = tree[ls(p)] + tree[rs(p)];
}

/// @brief 建树,将tag_set全部变为 -1 以初始化
/// @param p 根节点编号
/// @param pl 根节点对应的左儿子
/// @param pr 根节点对应的右儿子
void build(int p, int pl, int pr)
{
    tag_set[p] = -1;
    tag_rev[p] = 0;
    if (pl == pr)
        return;
    int mid = (pl + pr) >> 1;
    build(ls(p), pl, mid);
    build(rs(p), mid + 1, pr);
    push_up(p);
}

/// @brief 区间操作,全部置 d。如果翻转标记存在则将其归零
/// @param p 根节点编号
/// @param pl 根节点对应的左儿子
/// @param pr 根节点对应的右儿子
/// @param d  要区间变化的数字,可能为 1 或 0
void addTag_set(int p, int pl, int pr, int d)
{
    tag_set[p] = d;
    if (d == 1)
        tree[p] = pr - pl + 1;
    else
        tree[p] = 0;
    tag_rev[p] = 0;
}

/// @brief 区间操作,反转数字。1变成0,0变成1
/// @param p 根节点编号
/// @param pl 根节点对应的左儿子
/// @param pr 根节点对应的右儿子
void addTag_rev(int p, int pl, int pr)
{
    tag_rev[p] ^= 1;
    tree[p] = (pr - pl + 1) - tree[p];
}

/// @brief 下放标记,先赋值后翻转
/// @param p 根节点编号
/// @param pl 根节点对应的左儿子
/// @param pr 根节点对应的右儿子
void push_down(int p, int pl, int pr)
{
    if (tag_set[p] != -1)
    {
        int mid = (pl + pr) >> 1;
        addTag_set(ls(p), pl, mid, tag_set[p]);
        addTag_set(rs(p), mid + 1, pr, tag_set[p]);
        tag_set[p] = -1;
    }
    if (tag_rev[p])
    {
        int mid = (pl + pr) >> 1;
        addTag_rev(ls(p), pl, mid);
        addTag_rev(rs(p), mid + 1, pr);
        tag_rev[p] = 0;
    }
}

/// @brief 以赋值更新区间
/// @param L 区间左端点
/// @param R 区间右端点
/// @param p 根节点编号
/// @param pl 根节点对应的左儿子
/// @param pr 根节点对应的右儿子
/// @param d 赋值
void update_set(int L, int R, int p, int pl, int pr, int d)
{
    if (L <= pl && R >= pr)
    {
        addTag_set(p, pl, pr, d);
        return;
    }
    push_down(p, pl, pr);
    int mid = (pl + pr) >> 1;
    if (L <= mid)
        update_set(L, R, ls(p), pl, mid, d);
    if (R > mid)
        update_set(L, R, rs(p), mid + 1, pr, d);
    push_up(p);
}

/// @brief 翻转区间
/// @param L 区间左端点
/// @param R 区间右端点
/// @param p 根节点编号
/// @param pl 根节点对应的左儿子
/// @param pr 根节点对应的右儿子
void update_rev(int L, int R, int p, int pl, int pr)
{
    if (L <= pl && R >= pr)
    {
        addTag_rev(p, pl, pr);
        return;
    }
    push_down(p, pl, pr);
    int mid = (pl + pr) >> 1;
    if (L <= mid)
        update_rev(L, R, ls(p), pl, mid);
    if (R > mid)
        update_rev(L, R, rs(p), mid + 1, pr);
    push_up(p);
}

/// @brief 自顶向下,根据线段树来更新 ans 数组。如果区间和等于区间长度,则将范围内所有的 ans 置 1
/// @param L 区间左端点
/// @param R 区间右端点
/// @param p 根节点编号
/// @param pl 根节点对应的左儿子
/// @param pr 根节点对应的右儿子
void query(int L, int R, int p, int pl, int pr)
{
    push_down(p, pl, pr);
    push_up(p);
    if (pr - pl + 1 == tree[p])
    {
        for (int i = pl; i <= pr; i++)
            ans[i] = 1;
        return;
    }
    if (pl == pr)
        return;
    int mid = (pl + pr) >> 1;
    if (L <= mid)
        query(L, R, ls(p), pl, mid);
    if (R > mid)
        query(L, R, rs(p), mid + 1, pr);
}

char opt, ch1, ch2, temp; // 操作编号,左括号,右括号,逗号
int x, y;                 // 数字1, 数字2

int main()
{
    build(1, 0, N);
    while (cin >> opt >> ch1 >> x >> temp >> y >> ch2)
    {
        // 将端点加倍以处理开闭区间的情况。若为开区间还需额外增减。
        x = x * 2 + (ch1 == '(');
        y = y * 2 - (ch2 == ')');
        if (opt == 'U') // 并集:T范围内置1
            update_set(x, y, 1, 0, N, 1);
        else if (opt == 'I') // 交集:T的补集范围内置1
        {
            update_set(0, x - 1, 1, 0, N, 0);
            update_set(y + 1, N, 1, 0, N, 0);
        }
        else if (opt == 'D') // 减:T范围内置0
            update_set(x, y, 1, 0, N, 0);
        else if (opt == 'C') // 反减:T范围内翻转再执行I
        {
            update_rev(0, N, 1, 0, N);
            update_set(0, x - 1, 1, 0, N, 0);
            update_set(y + 1, N, 1, 0, N, 0);
        }
        else // 异或:范围内翻转
            update_rev(x, y, 1, 0, N);
    }
    query(0, N, 1, 0, N); // 更新 ans
    bool flag = true;     // 判断是否应该输出空集
    // 在 0 到 N 范围内,将连续的 ans 进行输出
    for (int i = 0; i <= N; i++)
    {
        int x, y;
        char ch1, ch2;
        if (ans[i] == 1)
        {
            flag = false;

            x = i;
            if (x % 2 == 0)
                ch1 = '[';
            else
                ch1 = '(';

            int j = i;
            while (ans[j] != 0)
                j++;
            y = j - 1;

            if (y % 2 == 0)
                ch2 = ']';
            else
            {
                ch2 = ')';
                y++;
            }

            printf("%c%d,%d%c ", ch1, x / 2, y / 2, ch2);
            i = j;
        }
    }
    if (flag)
        printf("empty set\n");
    return 0;
}
2024/3/27 08:27
加载中...