Educational Codeforces Round 23 F. MEX Queries(线段树区间设值)

题意:一个无限长的序列,q次操作,有三种操作:

1 l r : 将区间[l, r]设为1

2 l r : 将区间[l, r]设为0

3 l r : 将区间[l, r]的数反转(0变1, 1变0)

每次操作后输出最小的为0的位置下标

(q <= 1e5, l, r <= 1e18)


思路:前两个操作就是基础的线段数区间设置操作,关键是操作3怎么实现,线段树每个节点维护的是区间和,如果我们将它的区间反转,那和就变成了区间长度-原区间和。这样更新每个节点,就实现了反转。因为l, r比较大,所以需要离散化,因为可能的答案只是l 或 r+1所以只需要将它俩离散化,还有1可能为答案,还需要把1离散化。


代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5+5;
int n, treeSum[maxn*4], lazy[maxn*4], flag[maxn*4];
ll a[maxn], aa[maxn], Hash[maxn];

struct node
{
    int t;
    ll l, r;
}op[maxn];

void build(int root, int l, int r)
{
    treeSum[root] = 0;
    flag[root] = 0;
    lazy[root] = -1;
    if(l == r) return ;
    int mid = (l+r)/2;
    build(root*2, l, mid);
    build(root*2+1, mid+1, r);
}

void pushUp(int root)
{
    treeSum[root] = treeSum[root*2]+treeSum[root*2+1];
}

void pushDown(int root, int l, int r)
{
    int mid = (l+r)/2;
    if(lazy[root] != -1)    //区间设值标记下放,同时去除反转标记
    {
        lazy[root*2] = lazy[root*2+1] = lazy[root];
        treeSum[root*2] = (mid-l+1)*lazy[root];
        treeSum[root*2+1] = (r-mid)*lazy[root];
        flag[root*2] = flag[root*2+1] = 0;
        lazy[root] = -1;
    }
    if(flag[root])  //下放反转标记
    {
        flag[root*2] ^= 1;
        flag[root*2+1] ^= 1;
        treeSum[root*2] = (mid-l+1)-treeSum[root*2];
        treeSum[root*2+1] = (r-mid)-treeSum[root*2+1];
        flag[root] = 0;
    }
}

void Set(int root, int l, int r, int i, int j, int val)
{
    if(i <= l && j >= r)
    {
        treeSum[root] = (r-l+1)*val;
        lazy[root] = val;
        flag[root] = 0;
        return ;
    }
    pushDown(root, l, r);
    int mid = (l+r)/2;
    if(i <= mid) Set(root*2, l, mid, i, j, val);
    if(j > mid) Set(root*2+1, mid+1, r, i, j, val);
    pushUp(root);
}

void Reverse(int root, int l, int r, int i, int j)
{
    if(i <= l && j >= r)
    {
        treeSum[root] = (r-l+1)-treeSum[root];
        flag[root] ^= 1;
        return ;
    }
    pushDown(root, l, r);
    int mid = (l+r)/2;
    if(i <= mid) Reverse(root*2, l, mid, i, j);
    if(j > mid) Reverse(root*2+1, mid+1, r, i, j);
    pushUp(root);
}

ll Query(int root, int l, int r)
{
    if(l == r) return l;
    int mid = (l+r)/2;
    pushDown(root, l, r);
    if(treeSum[root*2] < mid-l+1) return Query(root*2, l, mid);
    else return Query(root*2+1, mid+1, r);
}


int main(void)
{
    while(cin >> n)
    {
        int cnt = 1;
        for(int i = 1; i <= n; i++)
        {
            scanf("%d%I64d%I64d", &op[i].t, &op[i].l, &op[i].r);
            op[i].r++;
            a[cnt] = aa[cnt] = Hash[cnt] = op[i].l;
            cnt++;
            a[cnt] = aa[cnt] = Hash[cnt] = op[i].r;
            cnt++;
        }
        a[cnt] = aa[cnt] = Hash[cnt] = 1;
        cnt++;
        cnt--;
        sort(Hash+1, Hash+1+cnt);
        int d = unique(Hash+1, Hash+1+cnt)-Hash-1;
        for(int i = 1; i <= cnt; i++)
            aa[i] = lower_bound(Hash+1, Hash+1+d, a[i])-Hash;
        build(1, 1, d);
        for(int i = 1; i <= n; i++)
        {
            int cmd = op[i].t;
            int l = aa[(i-1)*2+1];
            int r = aa[i*2]-1;  //注意要-1
            if(cmd == 1)
                Set(1, 1, d, l, r, 1);
            else if(cmd == 2)
                Set(1, 1, d, l, r, 0);
            else
                Reverse(1, 1, d, l, r);
            printf("%I64d\n", Hash[Query(1, 1, d)]);
        }
    }
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值