F. Machine Learning (带修改莫队)

https://codeforces.com/contest/940/problem/F

题意  给出n个数字,q个询问;

每次询问有两种类型,一种是询问区间,一种是单体修改;

询问区间是询问区间内最小的没用到的大于0的整数;

比如我有一串数字是 1 1 2 2 2 3    那么有两个1 三个2,一个3   出现次数分别有 两 三 一,  那么次数最小的没在区间内出现的是4;

对于这道题,除带修改莫队的模板之外,我们多加两个数组 vis cnt 

vis数组用来记录某个数出现的频率,cnt用来记录出现过的频率

那么每次询问的答案便是最小的没出现过的频率

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 const int maxn=2e5+10;
  4 struct Query{
  5     int l,r,Tim,ID;  //左右端点,时间戳,询问区间的序号
  6 }q[maxn];
  7 struct Change{
  8     int pos,New,Old;  //记录改变位置,改变前的值(颜色),改变后的值(颜色)
  9 }c[maxn];
 10 int s[maxn];  //记录在计算区间答案时,这一时刻的每个位置为何数(颜色)
 11 int now[maxn];  //记录在目前的时间戳下,区间上各个数的值(颜色);
 12 int ans[maxn];  //用来记录答案
 13 int l=1,r=0;  //将一开始的询问区间定为L=1,r=0;
 14 int block;
 15 int Hash;
 16 int cnt[maxn];
 17 int vis[maxn];
 18 map<int,int>mp;
 19 int get(int x)
 20 {
 21     if(!mp[x]) mp[x]=++Hash;
 22     return mp[x];
 23 }
 24 bool cmp(Query a,Query b)
 25 {
 26     if(a.l/block!=b.l/block) return a.l<b.l;
 27     if(a.r/block!=b.r/block) return a.r<b.r;
 28     return a.Tim<b.Tim;
 29 }
 30 void revise(int x,int d)
 31 {
 32     cnt[vis[x]]--;
 33     vis[x]+=d;
 34     cnt[vis[x]]++;
 35 }
 36 void going(int x,int d)
 37 {
 38     if(l<=x&&x<=r){  //如果改变的位置在目前的区间内
 39         revise(d,1);  //则将改变后的数(颜色)更新
 40         revise(s[x],-1);  //将此位置之前的数(颜色)-1;
 41     }
 42     s[x]=d; //把这个位置的权值改变
 43 }
 44 int cal()
 45 {
 46    // printf("hahaha\n");
 47     int base=1;
 48     while(cnt[base]){
 49         base++;
 50     }
 51     return base;
 52 }
 53 int main()
 54 {
 55     int n,m;
 56     scanf("%d%d",&n,&m);
 57     block=pow(n,2.0/3);  //分块,普通莫队是直接开根号;
 58                         //待修改莫队是n的3分之2次方
 59                         //至于为什么,便是数据测试出这样分块算起来比较快
 60                         //很抱歉本人不会算此复杂度
 61     for(int i=1;i<=n;i++){
 62         scanf("%d",&s[i]);
 63         now[i]=s[i]=get(s[i]);
 64     }
 65     //从这里还是跑询问区间;将询问操作放进sort排序;
 66     int Time=0,num=0;
 67     while(m--){
 68         int sign; int x,y;
 69         scanf("%d%d%d",&sign,&x,&y);
 70         //x为左端点,y为右端点,Time是时间戳,num表示第几个询问
 71         if(sign==1) q[++num]=(Query){x,y,Time,num};
 72         //x为修改位置,y为修改后的值,now[x]为修改前的值
 73         //最后再将目前此位置的值该为y;
 74         else{
 75             y=get(y);
 76             c[++Time]=(Change){x,y,now[x]},now[x]=y;
 77         }
 78     }
 79    // printf("111111111111111111111111\n");
 80     sort(q+1,q+num+1,cmp);
 81   //  printf("xxixiixixix\n");
 82     int T=0;l=1,r=0;    //初始化
 83     for(int i=1;i<=num;i++){
 84        // printf("hahahahhahaahha\n");
 85         //时间戳发生改变,则时间前移后移代码
 86         while(T<q[i].Tim) going(c[T+1].pos,c[T+1].New),T++;
 87         while(T>q[i].Tim) going(c[T].pos,c[T].Old),T--;
 88         //区间发生改变,左右端点移动操作
 89         while(l<q[i].l) revise(s[l],-1),l++;
 90         while(l>q[i].l) revise(s[l-1],1),l--;
 91         while(r<q[i].r) revise(s[r+1],1),r++;
 92         while(r>q[i].r) revise(s[r],-1),r--;
 93         //将答案记录下来
 94        // printf("111111111\n");
 95         ans[q[i].ID]=cal();
 96     }
 97     for(int i=1;i<=num;i++)
 98         printf("%d\n",ans[i]);
 99     return 0;
100 }

猜你喜欢

转载自www.cnblogs.com/pangbi/p/12397618.html