Codeforces 940F Machine Learning

14 篇文章 0 订阅
4 篇文章 0 订阅

Problem

CF可能不稳定,就写一下题意吧。

给定一个长度为n的序列,有m个操作。
对于1 l r,表示询问区间[l,r]内所有数的出现次数的mex值。
对于2 l x,表示修改a[l]=x。
n,m<=100000;a[i],x<=1000000000;
Notes:在区间内必定会有数字不出现,即出现次数为0,所以mex不可能为0。

Solution

先讲讲带修莫队的基本知识吧。块的大小为 n23 n 2 3 ,且排序的关键字分别是l所在块编号,r所在块编号和在这之前进行了多少次修改操作。为了得到修改前的值,我们在读入操作时,顺便模拟一遍。

我们可以用带修莫队做,如果能O(1)转移,那么复杂度为 O(n53) O ( n 5 3 ) ,极限数据下2e8,大概可以在2s内跑完。
首先1e9太大了,桶会爆,那么离散化一下。
本来的想法是O(logn)的转移,也就是维护一个堆来计算mex值。然而再乘上一个log就相当于乘上17的常数,然后就顺利地T飞了。
我们会发现在最坏情况下,就是有一个数出现了1次,有另一个数出现了2次,由于区间最长为n=100000,如此计算下来,mex的出现不可能比500更大。那么其实就没有必要去维护这个mex的答案了,我们就不妨把每个数出现次数再装入另一个桶里,即tt[i]表示出现了i次的数有几种,那么统计答案时,就可以直接线性扫描了,这个额外的复杂度是 O(qc500) O ( q c ∗ 500 ) ,即5e7,影响不大。

Code

#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#define rg register
using namespace std;
const int maxn=100010;
int n,m,sqr,qc,oc,l,r,tmp,cnt;
int a[maxn],t[maxn<<1],tt[maxn<<1],pos[maxn],lsh[maxn<<1],ans[maxn];
struct query{
    int l,r,up,id;
    bool operator < (const query &x)
    {
        if(pos[l]==pos[x.l]&&pos[r]==pos[x.r]) return up<x.up;
        if(pos[l]==pos[x.l]) return pos[r]<pos[x.r];
        return pos[l]<pos[x.l];
    }
}q[maxn];
struct oper{int pos,c[2],id;}op[maxn];
void input()
{
    static int tmp,l,r,len;
    scanf("%d%d",&n,&m);sqr=(int)pow(n,2.0/3);
    for(rg int i=1;i<=n;i++) scanf("%d",&a[i]),lsh[i]=a[i],pos[i]=(i-1)/sqr+1;
    memmove(t,a,sizeof(a));
    for(rg int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&tmp,&l,&r);
        if(tmp==1) q[++qc]=(query){l,r,oc,i};
        else
        {
            op[++oc].pos=l;op[oc].c[0]=a[l];op[oc].c[1]=r;
            op[oc].id=i;a[l]=r;lsh[n+i]=r;
        }
    }
    sort(lsh+1,lsh+n+m+1);len=unique(lsh+1,lsh+n+m+1)-lsh-1;
    for(rg int i=1;i<=n;i++) a[i]=lower_bound(lsh+1,lsh+len+1,t[i])-lsh;
    for(rg int i=1;i<=oc;i++)
    {
        op[i].c[0]=lower_bound(lsh+1,lsh+len+1,op[i].c[0])-lsh;
        op[i].c[1]=lower_bound(lsh+1,lsh+len+1,op[i].c[1])-lsh;
    }
    sort(q+1,q+qc+1);memset(t,0,sizeof(t));
}
inline void change(int x,int f)
{
    a[op[x].pos]=op[x].c[f];
    if(l<=op[x].pos&&op[x].pos<=r)
    {
        tt[t[op[x].c[f]]]--;tt[t[op[x].c[f^1]]]--;
        t[op[x].c[f]]++;t[op[x].c[f^1]]--;
        tt[t[op[x].c[f]]]++;tt[t[op[x].c[f^1]]]++;
    }
}
inline void update(int x,int f)
{
    tt[t[a[x]]]--;t[a[x]]+=f;tt[t[a[x]]]++;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif
    input();
    l=1;r=0;cnt=0;
    for(rg int i=1;i<=qc;i++)
    {
        while(op[cnt+1].id<q[i].id&&cnt<oc) change(++cnt,1);
        while(op[cnt].id>q[i].id) change(cnt--,0);
        while(l>q[i].l) update(--l,1);
        while(r<q[i].r) update(++r,1);
        while(l<q[i].l) update(l++,-1);
        while(r>q[i].r) update(r--,-1);
        for(rg int j=1;j<=510;j++)
          if(!tt[j]){ans[q[i].id]=j;break;}
    }
    for(rg int i=1;i<=m;i++)
      if(ans[i]) printf("%d\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值