Machine Learning(CF-940F)

Problem Description

You come home and fell some unpleasant smell. Where is it coming from?

You are given an array a. You have to answer the following queries:

You are given two integers l and r. Let ci be the number of occurrences of i in al: r, where al: r is the subarray of a from l-th element to r-th inclusive. Find the Mex of {c0, c1, ..., c109}
You are given two integers p to x. Change ap to x.
The Mex of a multiset of numbers is the smallest non-negative integer not in the set.

Note that in this problem all elements of a are positive, which means that c0 = 0 and 0 is never the answer for the query of the second type.

Input

The first line of input contains two integers n and q (1 ≤ n, q ≤ 100 000) — the length of the array and the number of queries respectively.

The second line of input contains n integers — a1, a2, ..., an (1 ≤ ai ≤ 109).

Each of the next q lines describes a single query.

The first type of query is described by three integers ti = 1, li, ri, where 1 ≤ li ≤ ri ≤ n — the bounds of the subarray.

The second type of query is described by three integers ti = 2, pi, xi, where 1 ≤ pi ≤ n is the index of the element, which must be changed and 1 ≤ xi ≤ 109 is the new value.

Output

For each query of the first type output a single integer  — the Mex of {c0, c1, ..., c109}.

Sample Input

10 4
1 2 3 1 1 2 2 2 9 9
1 1 1
1 2 8
2 7 1
1 2 8

Sample Output

2
3
2

题意:给出一个长度为 n 的数组以及 m 个询问,对于每个询问 q 有两种操作,q 为 1 时查询区间 [l,r] 中每个数字出现次数的 mex,q 为 2 时将位置 p 的值修改为 x,其中 mex 代表一些数字中最小的未出现的自然数

思路:带修莫队+离散化

这个题实际上是 数颜色(洛谷-P1903) 的变种,由于需要统计出现次数需要使用桶排,而数值最大达到 1E9,因此需要离散化,离散化后进行带修莫队即可,最后暴力求 mex

Source Program

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#define PI acos(-1.0)
#define E 1e-9
#define INF 0x3f3f3f3f
#define LL long long
const int MOD=10007;
const int N=200000+5;
const int dx[]= {-1,1,0,0};
const int dy[]= {0,0,-1,1};
using namespace std;

struct Node{
    int l,r;//询问的左右端点
    int time;//时间维度
    int id;//询问的编号
}q[N];
struct Change{
    int pos;//要修改的位置
    int col;//要改成的值
}c[N];
int n,m,a[N],temp[N];
int block;//分块
int numQ,numC;//查询、修改操作的次数
int cnt[N],sum[N];
int res[N];

bool cmp(Node a,Node b){//排序
    return (a.l/block)^(b.l/block) ? a.l<b.l : ((a.r/block)^(b.r/block)?a.r<b.r:a.time<b.time);
}

void add(int x){//统计新的,根据具体情况修改
    sum[cnt[x]]--;
    cnt[x]++;
    sum[cnt[x]]++;
}
void del(int x){//减去旧的,根据具体情况修改
    sum[cnt[x]]--;
    cnt[x]--;
    sum[cnt[x]]++;
}
void change(int x,int ti){//改变时间轴
    if(c[ti].pos>=q[x].l&&c[ti].pos<=q[x].r){
        del(a[c[ti].pos]);//删除指定位置上的值
        add(c[ti].col);//统计新的数
    }
    swap(c[ti].col,a[c[ti].pos]);//直接互换,下次执行时必定是回退这次操作
}
int main(){
    while(scanf("%d%d",&n,&m)!=EOF){
        numQ=0;
        numC=0;
        memset(sum,0,sizeof(sum));
        memset(cnt,0,sizeof(cnt));
        block=pow(n,0.66666);//分块

        int numTemp=0;
        for(int i=1;i<=n;++i){
            scanf("%d",&a[i]);
            temp[++numTemp]=a[i];
        }
        for(int i=1;i<=m;i++){
            int op;
            scanf("%d",&op);
            if(op==1){//查询操作
                ++numQ;//查询操作次数+1
                scanf("%d%d",&q[numQ].l,&q[numQ].r);
                q[numQ].id=numQ;//序号
                q[numQ].time=numC;//时间轴
            }
            else{//修改操作
                ++numC;//修改操作次数+1
                scanf("%d%d",&c[numC].pos,&c[numC].col);
                temp[++numTemp]=c[numC].col;
            }
        }

        //离散化
        sort(temp+1,temp+1+numTemp);
        numTemp=unique(temp+1,temp+1+numTemp)-temp;
        for(int i=1;i<=n;i++)
            a[i]=lower_bound(temp+1,temp+1+numTemp,a[i])-temp;
        for(int i=1;i<=numC;i++)
            c[i].col=lower_bound(temp+1,temp+1+numTemp,c[i].col)-temp;

        sort(q+1,q+numQ+1,cmp);//对询问进行排序
        int l=1,r=0,time=0;//左右指针与时间轴
        for(int i=1;i<=numQ;i++){
            int ql=q[i].l,qr=q[i].r;//询问的左右端点
            int qtime=q[i].time;//询问的时间轴
            while(l>ql) add(a[--l]);//[l-1,r,time]
            while(l<ql) del(a[l++]);//[l+1,r,time]
            while(r<qr) add(a[++r]);//[l,r+1,time]
            while(r>qr) del(a[r--]);//[l,r-1,time]
            while(time<qtime) change(i,++time);//[l,r,time+1]
            while(time>qtime) change(i,time--);//[l,r,time-1]

            res[q[i].id]=1;
            while(sum[res[q[i].id]]>0)
                res[q[i].id]++;
        }

        for(int i=1;i<=numQ;i++)
            printf("%d\n",res[i]);
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值