codeforces CF817F MEX Queries 线段树 离散化

本文详细解析了Codeforces题目F.MEXQueries,介绍了如何使用线段树和离散化解决此问题,包括操作1和2的区间赋值,操作3的区间反转,以及如何维护线段树区间长度并进行二分查找最小未出现正整数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

$ \Rightarrow $ 戳我进CF原题

F. MEX Queries

time limit per test: 2 seconds
memory limit per test: 256 megabytes
input: standard input
output: standard output

 

You are given a set of integer numbers, initially it is empty. You should perform $ n $ queries.
There are three different types of queries:
 

  • $ 1 \quad l \quad r $ — Add all missing numbers from the interval $ [l, r] $
  • $ 2 \quad l \quad r $ — Remove all present numbers from the interval $ [l, r] $
  • $ 3 \quad l \quad r $ — Invert the interval $ [l, r] $ — add all missing and remove all present numbers from the interval $ [l, r] $
     

After each query you should output $ MEX $ of the set
— the smallest positive $ (MEX  ≥ 1) $ integer number which is not presented in the set.
 

Input

The first line contains one integer number $ n (1 ≤ n ≤ 10^5) $ .
Next $ n $ lines contain three integer numbers $ t, l, r (1 ≤ t ≤ 3, 1 ≤ l ≤ r ≤ 10^18) $ — type of the query, left and right bounds.
 

Output

Print $ MEX $ of the set after each query.
 

Examples

input1
 3
 1 3 4
 3 1 6
 2 1 3
output1
 1 
 3
 1

input2
 4
 1 1 3
 3 5 6
 2 4 4
 3 1 6
output2
 4
 4
 4
 1

 

Note

Here are contents of the set after each query in the first example:

  1. $ (3, 4) $ — the interval $ [3, 4] $ is added
  2. $ (1, 2, 5, 6) $ — numbers $ (3, 4) $ from the interval $ [1, 6] $ got deleted and all the others are added
  3. $ (5, 6) $ — numbers $ (1, 2) $ got deleted
     

题目大意

  • 给你一个无限长的数组,初始的时候都为 $ 0 $ ,
    操作 $ 1 $ 是把给定区间设为 $ 1 $ ,
    操作 $ 2 $ 是把给定区间清零,
    操作 $ 3 $ 是把给定区间反转。
    每次操作后要输出最小位置的 $ 0 $ 。

  • 数据范围:$ 1 \le n \le 10^5 $
     

思路

  • 线段树+离散化

  • 操作 $ 1 $ 和 $ 2 $ :区间赋值

  • 操作 $ 3 $ :相当于区间每个数 $ xor \quad 1 $

  • 可以维护线段树的区间长度,线段树上二分即可

  • 注意:
  1. 求的是 $ mex $ ,可能不是 $ l_i,r_i $ 中的任何一个,对于 $ 1,l_{i+1},r_{i+1} $ 也要离散。
  2. 记录原来信息的数组必须得开 $ long \quad long $
     

代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
#define ll long long
#define N 400005
inline ll read() {
    register char ch;
    while(!isdigit(ch=getchar()));
    register ll x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return x;
}
int n,m,opt[N];
ll a[N<<2],ql[N],qr[N],sum[N<<2],lzy[N<<2],rev[N<<2];
inline void pushdown(int o,int l,int r){
    int mid=l+r>>1;
    if(lzy[o]){
        lzy[o<<1]=lzy[o<<1|1]=lzy[o]--;
        sum[o<<1]=(mid-l+1)*lzy[o];
        sum[o<<1|1]=(r-mid)*lzy[o];
        lzy[o]=rev[o<<1]=rev[o<<1|1]=0;
    }
    if(rev[o]){
        sum[o<<1]=mid-l+1-sum[o<<1];
        sum[o<<1|1]=r-mid-sum[o<<1|1];
        rev[o<<1]^=1;
        rev[o<<1|1]^=1;
        rev[o]=0;
    }
}
void updata(int o,int l,int r,int L,int R,int val){
    if(L<=l&&r<=R){
        lzy[o]=val--; rev[o]=0;
        sum[o]=(val)*(r-l+1);
        return;
    }
    pushdown(o,l,r);
    int mid=l+r>>1;
    if(L>mid) updata(o<<1|1,mid+1,r,L,R,val);
    else if(R<=mid) updata(o<<1,l,mid,L,R,val);
    else{
        updata(o<<1,l,mid,L,R,val);
        updata(o<<1|1,mid+1,r,L,R,val);
    }
    sum[o]=sum[o<<1]+sum[o<<1|1];
}
void modify(int o,int l,int r,int L,int R){
    if(L<=l&&r<=R){
        sum[o]=r-l+1-sum[o];
        rev[o]^=1;
        return;
    }
    pushdown(o,l,r);
    int mid=l+r>>1;
    if(L>mid) modify(o<<1|1,mid+1,r,L,R);
    else if(R<=mid) modify(o<<1,l,mid,L,R);
    else{
        modify(o<<1,l,mid,L,R);
        modify(o<<1|1,mid+1,r,L,R);
    }
    sum[o]=sum[o<<1]+sum[o<<1|1];   
}
ll query(int o,int l,int r){
    if(l==r) return a[l];
    pushdown(o,l,r);
    int mid=l+r>>1;
    ll res;
    if(sum[o<<1]!=mid-l+1) res=query(o<<1,l,mid);
    else res=query(o<<1|1,mid+1,r);
    sum[o]=sum[o<<1]+sum[o<<1|1];
    return res;
}
signed main(){
    n=read(); a[++m]=1;
    for(int i=1;i<=n;++i){
        opt[i]=read(); ql[i]=read(); qr[i]=read();
        a[++m]=ql[i]; a[++m]=ql[i]+1;
        a[++m]=qr[i]; a[++m]=qr[i]+1;
    }
    sort(a+1,a+1+m);
    m=unique(a+1,a+1+m)-(a+1);
    for(int i=1;i<=n;++i){
        int L=lower_bound(a+1,a+1+m,ql[i])-a;
        int R=lower_bound(a+1,a+1+m,qr[i])-a;
        if(opt[i]==1||opt[i]==2) updata(1,1,m,L,R,opt[i]==1 ? 2 : 1);
        else modify(1,1,m,L,R);
        printf("%lld\n",query(1,1,m));
    }
    return 0;
}
/*
#        42702002
When     2018-09-09 14:03:14 
Who      PotremZ 
Problem  F - MEX Queries 
Lang     GNU C++11
Verdict  Accepted 
Time     264 ms 
Memory   57900 KB 
*/

转载于:https://www.cnblogs.com/PotremZ/p/CF817F.html

Codeforces 2123F 问题中,目标是通过重新排列数组 $ a $ 来最小化不动点的数量。所谓“不动点”是指在重新排列后的数组中满足 $ a_i = i $ 的位置。该问题要求设计一种策略,以最优方式重新排列数组元素,使得这样的不动点数量最少。 为了解决这个问题,可以采用贪心算法和图论思想相结合的策略: - 首先,观察到如果某个值 $ i $ 出现了多次(即 $ a_i = i $),那么这些重复的值必须被移动到其他位置,以消除不动点。 - 对于那些没有出现在其索引上的值(例如 $ a_i \neq i $),可以通过交换操作将其移动到合适的位置,从而避免产生新的不动点。 一个有效的解决方案可以基于以下步骤: 1. 构建一个映射表,记录每个值出现的位置。 2. 找出所有当前值等于其索引的位置(即当前的不动点)。 3. 尝试通过交换来消除这些不动点。优先考虑将这些值移动到未被占用的位置,并确保不会引入新的不动点。 4. 在无法完全消除所有不动点的情况下,选择最优的交换策略以尽可能减少不动点的数量。 以下是 Python 中的一个示例实现,用于解决此类问题的基本思路: ```python def minimize_fixed_points(n, a): pos = {} fixed_points = [] # 记录每个值的出现位置,并找出初始的不动点 for i in range(n): if a[i] == i + 1: fixed_points.append(i) if a[i] not in pos: pos[a[i]] = [] pos[a[i]].append(i) # 如果没有重复的值,则可以直接交换以消除所有不动点 result = a[:] for i in fixed_points: found = False for val in pos: if val != i + 1 and len(pos[val]) > 0: j = pos[val].pop() result[i], result[j] = result[j], result[i] found = True break if not found: # 特殊情况处理:当只剩下一个值时 for j in range(n): if j != i and result[j] != j + 1: result[i], result[j] = result[j], result[i] break return result # 示例输入 n = int(input()) a = list(map(int, input().split())) result = minimize_fixed_points(n, a) print(' '.join(map(str, result))) ``` 此代码实现了上述逻辑,并尝试通过交换来最小化不动点的数量。对于大多数情况,它能够有效消除所有不动点;在某些特殊情况下(例如所有值都唯一且存在多个不动点),则需要特别处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值