标签:数组 space bit 次方 两种 为什么 get 改变 左右
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 }
标签:数组 space bit 次方 两种 为什么 get 改变 左右
原文地址:https://www.cnblogs.com/pangbi/p/12397618.html