[SHOI2009] 会场预约

luogu2161
省选 / NOI-

题面:
设计一个数据结构,支持两种操作:
1. A i j
添加一个新的预约[i, j],并删除所有与其冲突的预约。返回此次操作删除的预约的个数。
2. B
返回当前的预约总数。
数据范围:操作数<=200000

输入输出样例

思路:
线段树+染色
1、颜色。每个预约均用一种颜色代表

struct tColor
{
    int left, right, num;
    //表示区间[left, righe] 为num色
} color[MAXN];

2、线段树。包含添加颜色功能(包含了删除颜色), 其中删除颜色中又包含了单点修改(为了修改sumv值)

struct Segment_Tree
{
    int colorv[MAXN*4], coloropt[MAXN*4]; 
    //colorv:当前区间的颜色(-1:无色, -2:杂色, 1~n:某种颜色);
    //coloropt:改变颜色的懒惰标记, 默认为0
    #define lson (o<<1)
    #define rson (o<<1|1)

常规的pushup, pushdown, build, change函数:

    inline void pushup(int o)
    {
        if(colorv[lson] == colorv[rson]) colorv[o] = colorv[lson];
        else colorv[o] = -2; //标记为杂色
        return ;
    } //向上推颜色
    inline void pushdown(int o)
    {
        if(coloropt[o] == 0) return ;
        colorv[lson] = coloropt[o];
        colorv[rson] = coloropt[o];
        //将颜色向下推
        coloropt[lson] = coloropt[o];
        coloropt[rson] = coloropt[o];
        //将标记向下推
        coloropt[o] = 0;
        //初始化标记
        return ;
    }
    inline void build(int o, l, r) //建树, 在此题中仅作初始化用
    memset(colorv, -1, sizeof(colorv)); //-1:无色
    memset(coloropt, 0, sizeof(coloropt)); //0:无标记
    memset(sumv, 0, sizeof(sumv));
    return ;
    inline void change(int o, int l, int r, int k, int v) //单点修改, 和其他线段树的单点修改并无太大差异, 但仅修改sumv值; k:修改的点的位置, v:修改后的值 
    { ... }

删除颜色:

    inline void deletecolor(int o, int l, int r, int ql, int qr) //删除一种颜色, ql=color[n].left, qr=color[n].right
    {
        if(l>=ql && r<=qr)
        {
            colorv[o] = -1;
            coloropt[o] = -1;
            return ;
        } //将color范围内的颜色全部删除, 因为不存在一个区间有多种颜色的情况, 所以不用考虑杂色的问题

        pushdown(o, l, r);
        int mid = (l+r) >>1;
        if(mid>=ql) deletecolor(lson, l, mid, ql, qr);
        if(mid+1<=qr) deletecolor(rson, mid+1, r, ql, qr);
        pushup(o);
        return ; 
    }

添加颜色
每一次添加颜色时,都要搜索到该颜色所在的区间,所以在删除颜色时,删除区间可能比添加的区间要大,导致删除的标记被打在染色标记上面。这时pushdown便会把 now_color覆盖所以每次删除后,都直接退出至主函数(利用done), 然后再重头搜索一遍:

    inline int addcolor(int o, int l, int r, int ql, int qr) //添加颜色
    {
        if(l>=ql && r<=qr && colorv[o] != -2) //如果当前节点有多个颜色, 则继续向下搜索(-2代表杂色)
        {
            if(colorv[o] == -1) { colorv[o] = now_color; coloropt[o] = now_color; return 0; } //如果该节点无色, 则直接打上标记
            if(colorv[o] == now_color) return 0; //如果该节点与将要修改的节点的颜色相同, 则直接跳过
            available --; //available:当前有效的颜色数

            int tmpl = color[tmpcolor].left, tmpr = color[tmpcolor].right;
            deletecolor(1, 1, TREE_WIDTH, tmpl, tmpr);
            done = true;
            return 1;
        }

        pushdown(o, l, r);
        int mid = (l+r) >>1, ans = 0;
        if(mid>=ql) ans += addcolor(lson, l, mid, ql, qr);
        if(done == true) return ans ;
        if(mid+1<=qr) ans += addcolor(rson, mid+1, r, ql, qr);
        pushup(o);
        return ans;
    }
} st;

int main()
{
    ...
    do {
        done = false;
        tmpans += st.addcolor(1, 1, TREE_WIDTH, l, r);
    } while(donw == true);
    printf("%d\n", tmpans);
    ...
}

此做法较为暴力,luogu #10跑了800+ms, 数据如果再强一点应该就会TLE。因为每次删除完一个点之后就会重新从头开始跑一次,可以在这里着手优化:若第一次搜索到一个颜色并满足下面的条件,则直接删除,继续pushdown。

((l>=ql && l<=qr) || (r>=ql && r<=qr)) && colorv[o]!=-1 && colorv[o]!=-2 && colorv[o]!=now_color
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值