题目描述

小J是国家图书馆的一位图书管理员,他的工作是管理一个巨大的书架。虽然他很能吃苦耐劳,但是由于这个书架十分巨大,所以他的工作效率总是很低,以致他面临着被解雇的危险,这也正是他所郁闷的。

具体说来,书架由N个书位组成,编号从1到N。每个书位放着一本书,每本书有一个特定的编码。

小J的工作有两类:

1.图书馆经常购置新书,而书架任意时刻都是满的,所以只得将某位置的书拿掉并换成新购的书。

2.小J需要回答顾客的查询,顾客会询问某一段连续的书位中某一特定编码的书有多少本。

例如,共5个书位,开始时书位上的书编码为1,2,3,4,5

一位顾客询问书位1到书位3中编码为“2”的书共多少本,得到的回答为:1

一位顾客询问书位1到书位3中编码为“1”的书共多少本,得到的回答为:1

此时,图书馆购进一本编码为“1”的书,并将它放到2号书位。

一位顾客询问书位1到书位3中编码为“2”的书共多少本,得到的回答为:0

一位顾客询问书位1到书位3中编码为“1”的书共多少本,得到的回答为:2

……

你的任务是写一个程序来回答每个顾客的询问。

输入输出格式

输入格式:

第一行两个整数N,M,表示一共N个书位,M个操作。

接下来一行共N个整数数A1,A2…AN,Ai表示开始时位置i上的书的编码。

接下来M行,每行表示一次操作,每行开头一个字符

若字符为‘C’,表示图书馆购进新书,后接两个整数A(1<=A<=N),P,表示这本书被放在位置A上,以及这本书的编码为P。

若字符为‘Q’,表示一个顾客的查询,后接三个整数A,B,K(1<=A<=B<=N),表示查询从第A书位到第B书位(包含A和B)中编码为K的书共多少本。

输出格式:

对每一个顾客的查询,输出一个整数,表示顾客所要查询的结果。

输入输出样例

输入样例#1:

  1. 5 5
  2. 1 2 3 4 5
  3. Q 1 3 2
  4. Q 1 3 1
  5. C 2 1
  6. Q 1 3 2
  7. Q 1 3 1
输出样例#1:

  1. 1
  2. 1
  3. 0
  4. 2

说明

对于40%的数据,1<=N,M<=5000

对于100%的数据,1<=N,M<=100000

对于100%的数据,所有出现的书的编码为不大于2147483647的正数。

Solution:

  本题解法太多,前后用了4种方法去做,由简入繁。

  法一:分块+map(736ms)

  我们可以将数列划分为$\sqrt n$块,每个块用map维护块内元素出现次数,那么单次修改可以做到$O(\log(\sqrt n))$,单次查询能做到$\sqrt n \log (\sqrt n)$。时间复杂度$O(n\sqrt n \log(\sqrt n))$,极限数据能卡到$2e8$,但是本题数据比较水也能过。

  法一代码:

  1. /*Code by 520 -- 10.28*/
  2. #include<bits/stdc++.h>
  3. #include<ext/pb_ds/assoc_container.hpp>
  4. #include<ext/pb_ds/hash_policy.hpp>
  5. #define il inline
  6. #define ll long long
  7. #define RE register
  8. #define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++)
  9. #define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--)
  10. using namespace std;
  11. using namespace __gnu_pbds;
  12. const int N=;
  13. gp_hash_table<int,int>mp[];
  14. int n,m,a[N],bl[N],ln[N],rn[N],clo,u,v,w;
  15. char opt[];
  16.  
  17. int gi(){
  18. int a=;char x=getchar();
  19. while(x<''||x>'') x=getchar();
  20. while(x>=''&&x<='') a=(a<<)+(a<<)+(x^),x=getchar();
  21. return a;
  22. }
  23.  
  24. il int query(int x,int y,int z){
  25. int bx=bl[x],by=bl[y],res=;
  26. if(bx==by) {
  27. For(i,x,y) res+=(a[i]==z);
  28. return res;
  29. }
  30. For(i,bx+,by-) res+=mp[i][z];
  31. For(i,x,rn[bx]) res+=(a[i]==z);
  32. For(i,ln[by],y) res+=(a[i]==z);
  33. return res;
  34. }
  35.  
  36. int main(){
  37. n=gi(),m=gi(); clo=sqrt(n);
  38. For(i,,n) {
  39. a[i]=gi(),bl[i]=(i-)/clo+,mp[bl[i]][a[i]]++;
  40. if(!ln[bl[i]]) ln[bl[i]]=i;
  41. rn[bl[i]]=i;
  42. }
  43. For(i,,m){
  44. scanf("%s",opt);
  45. if(opt[]=='Q') u=gi(),v=gi(),w=gi(),printf("%d\n",query(u,v,w));
  46. else {
  47. u=gi(),v=gi();
  48. mp[bl[u]][a[u]]--;
  49. a[u]=v;
  50. mp[bl[u]][a[u]]++;
  51. }
  52. }
  53. return ;
  54. }

  

  法二:分块+离散化(383ms)

  我们显然可以用奇技淫巧优化掉法一中的$\log(\sqrt n)$。只需要离线操作,并对数的值域离散,然后用空间换时间,一种方法是把块数调小,另一种是直接用short类型来开桶(反正一个块内的元素次数不会超过$\sqrt n<2^{16}-1$),能卡着空间过。时间复杂度$O(n\sqrt n)$。

  法二代码:  

  1. /*Code by 520 -- 10.28*/
  2. #include<bits/stdc++.h>
  3. #define il inline
  4. #define ll long long
  5. #define RE register
  6. #define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++)
  7. #define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--)
  8. using namespace std;
  9. const int N=;
  10. int n,m,a[N],bl[N],ln[N],rn[N],clo,u,v,w,*q[N<<],cnt;
  11. struct node{
  12. int l,r,x;
  13. }t[N];
  14. short mp[][N<<];
  15. char opt[N][];
  16.  
  17. int gi(){
  18. int a=;char x=getchar();
  19. while(x<''||x>'') x=getchar();
  20. while(x>=''&&x<='') a=(a<<)+(a<<)+(x^),x=getchar();
  21. return a;
  22. }
  23.  
  24. il bool cmp(int *a,int *b){return *a<*b;}
  25.  
  26. il int query(int x,int y,int z){
  27. int bx=bl[x],by=bl[y],res=;
  28. if(bx==by) {
  29. For(i,x,y) res+=(a[i]==z);
  30. return res;
  31. }
  32. For(i,bx+,by-) res+=mp[i][z];
  33. For(i,x,rn[bx]) res+=(a[i]==z);
  34. For(i,ln[by],y) res+=(a[i]==z);
  35. return res;
  36. }
  37.  
  38. int main(){
  39. n=gi(),m=gi(); clo=sqrt(n);
  40. For(i,,n) {
  41. a[i]=gi(),q[++cnt]=&a[i],bl[i]=(i-)/clo+;
  42. if(!ln[bl[i]]) ln[bl[i]]=i;
  43. rn[bl[i]]=i;
  44. }
  45. For(i,,m){
  46. scanf("%s",opt[i]);
  47. if(opt[i][]=='Q') t[i]=node{gi(),gi(),gi()},q[++cnt]=&t[i].x;
  48. else t[i]=node{gi(),gi(),},q[++cnt]=&t[i].r;
  49. }
  50. sort(q+,q+cnt+,cmp); int lst=-,tot=;
  51. For(i,,cnt) if(*q[i]!=lst) lst=*q[i],*q[i]=++tot; else *q[i]=tot;
  52. For(i,,n) mp[bl[i]][a[i]]++;
  53. For(i,,m){
  54. if(opt[i][]=='Q') printf("%d\n",query(t[i].l,t[i].r,t[i].x));
  55. else {
  56. u=t[i].l,v=t[i].r;
  57. mp[bl[u]][a[u]]--;
  58. a[u]=v;
  59. mp[bl[u]][a[u]]++;
  60. }
  61. }
  62. return ;
  63. }

  

  法三:带修改主席树(1156ms )

  本题显然是个带修主席树的板子,只需要离线操作并对值域离散,然后就直接板子咯。时间复杂度$O(n\log^2 n)$。

  法三代码:

  1. /*Code by 520 -- 10.28*/
  2. #include<bits/stdc++.h>
  3. #define il inline
  4. #define ll long long
  5. #define RE register
  6. #define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++)
  7. #define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--)
  8. using namespace std;
  9. const int N=;
  10. int n,m,a[N],*q[N<<],cnt,tot,rt[N],X[N],Y[N],tx,ty;
  11. struct query{
  12. int l,r,x;
  13. }qus[N];
  14. struct node{
  15. int ls,rs,sz;
  16. }t[N*];
  17. char opt[N][];
  18.  
  19. int gi(){
  20. int a=;char x=getchar();
  21. while(x<''||x>'') x=getchar();
  22. while(x>=''&&x<='') a=(a<<)+(a<<)+(x^),x=getchar();
  23. return a;
  24. }
  25.  
  26. il bool cmp(int *a,int *b){return *a<*b;}
  27.  
  28. void ins(int l,int r,int k,int x,int lst,int &rt){
  29. if(!rt) rt=++tot; t[rt]=t[lst],t[rt].sz+=x;
  30. if(l==r) return;
  31. int m=l+r>>;
  32. if(k<=m) ins(l,m,k,x,t[lst].ls,t[rt].ls);
  33. else ins(m+,r,k,x,t[lst].rs,t[rt].rs);
  34. }
  35.  
  36. il void update(int i,int v){
  37. int k=a[i];
  38. while(i<=n) ins(,cnt,k,v,rt[i],rt[i]),i+=i&-i;
  39. }
  40.  
  41. il int calc(int x){
  42. int l=,r=cnt,k=qus[x].x,res=;
  43. tx=ty=;
  44. for(RE int i=qus[x].l-;i;i-=i&-i) X[++tx]=rt[i];
  45. for(RE int i=qus[x].r;i;i-=i&-i) Y[++ty]=rt[i];
  46. while(){
  47. int mid=l+r>>;
  48. if(l==r) break;
  49. if(mid>=k) {
  50. r=mid;
  51. For(i,,tx) X[i]=t[X[i]].ls;
  52. For(i,,ty) Y[i]=t[Y[i]].ls;
  53. }
  54. else {
  55. l=mid+;
  56. For(i,,tx) X[i]=t[X[i]].rs;
  57. For(i,,ty) Y[i]=t[Y[i]].rs;
  58. }
  59. }
  60. For(i,,ty) res+=t[Y[i]].sz;
  61. For(i,,tx) res-=t[X[i]].sz;
  62. return res;
  63. }
  64.  
  65. int main(){
  66. n=gi(),m=gi();
  67. For(i,,n) a[i]=gi(),q[++cnt]=&a[i];
  68. For(i,,m){
  69. scanf("%s",opt[i]);
  70. if(opt[i][]=='Q') qus[i]=query{gi(),gi(),gi()},q[++cnt]=&qus[i].x;
  71. else qus[i]=query{gi(),gi(),},q[++cnt]=&qus[i].r;
  72. }
  73. sort(q+,q+cnt+,cmp); int lst=-;
  74. For(i,,cnt) if(*q[i]!=lst) lst=*q[i],*q[i]=++tot; else *q[i]=tot;
  75. cnt=tot;
  76. memset(&t[tot=],,sizeof(t[]));
  77. For(i,,n) update(i,);
  78. For(i,,m){
  79. if(opt[i][]=='Q') printf("%d\n",calc(i));
  80. else {
  81. int u=qus[i].l,v=qus[i].r;
  82. update(u,-),a[u]=v,update(u,);
  83. }
  84. }
  85. return ;
  86. }

  法四:平衡树(432ms)

  我们离线操作并对值域离散后,可以直接用无旋treap维护每个值域的下标中序,那么修改就是简单的删除操作,查询也是简单的分离操作。时间复杂度$O(n\log n)$。

  法四代码:

  1. /*Code by 520 -- 10.28*/
  2. #include<bits/stdc++.h>
  3. #define il inline
  4. #define ll long long
  5. #define RE register
  6. #define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++)
  7. #define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--)
  8. using namespace std;
  9. const int N=;
  10. int n,m,a[N],*q[N<<],cnt;
  11. int ch[N][],rt[N],rnd[N],date[N],siz[N];
  12. struct node{
  13. int l,r,x;
  14. }t[N];
  15. char opt[N][];
  16.  
  17. int gi(){
  18. int a=;char x=getchar();
  19. while(x<''||x>'') x=getchar();
  20. while(x>=''&&x<='') a=(a<<)+(a<<)+(x^),x=getchar();
  21. return a;
  22. }
  23.  
  24. il bool cmp(int *a,int *b){return *a<*b;}
  25.  
  26. il int newnode(int v){
  27. ++cnt;
  28. siz[cnt]=,date[cnt]=v,rnd[cnt]=rand();
  29. return cnt;
  30. }
  31.  
  32. il void up(int rt){siz[rt]=siz[ch[rt][]]+siz[ch[rt][]]+;}
  33.  
  34. int merge(int x,int y){
  35. if(!x||!y) return x+y;
  36. if(rnd[x]<rnd[y]) {ch[x][]=merge(ch[x][],y),up(x);return x;}
  37. else {ch[y][]=merge(x,ch[y][]),up(y);return y;}
  38. }
  39.  
  40. void split(int rt,int v,int &x,int &y){
  41. if(!rt) {x=y=;return;}
  42. if(date[rt]<=v) x=rt,split(ch[rt][],v,ch[x][],y),up(x);
  43. else y=rt,split(ch[rt][],v,x,ch[y][]),up(y);
  44. }
  45.  
  46. il void ins(int k,int v){
  47. int x,y; split(rt[k],v,x,y),rt[k]=merge(merge(x,newnode(v)),y);
  48. }
  49.  
  50. il void del(int k,int v){
  51. int x,y,z; split(rt[k],v,x,y),split(x,v-,x,z),rt[k]=merge(x,y);
  52. }
  53.  
  54. int main(){
  55. srand(time());
  56. n=gi(),m=gi();
  57. For(i,,n) a[i]=gi(),q[++cnt]=&a[i];
  58. For(i,,m){
  59. scanf("%s",opt[i]);
  60. if(opt[i][]=='Q') t[i]=node{gi(),gi(),gi()},q[++cnt]=&t[i].x;
  61. else t[i]=node{gi(),gi(),},q[++cnt]=&t[i].r;
  62. }
  63. sort(q+,q+cnt+,cmp); int lst=-,tot=;
  64. For(i,,cnt) if(*q[i]!=lst) lst=*q[i],*q[i]=++tot; else *q[i]=tot;
  65. cnt=;
  66. For(i,,n) ins(a[i],i);
  67. For(i,,m){
  68. if(opt[i][]=='Q') {
  69. int x,y,z;
  70. split(rt[t[i].x],t[i].r,x,y),split(x,t[i].l-,x,z);
  71. printf("%d\n",siz[z]);
  72. rt[t[i].x]=merge(merge(x,z),y);
  73. }
  74. else {
  75. int x,y,z,u=t[i].l,v=t[i].r;
  76. del(a[u],u),a[u]=v,ins(a[u],u);
  77. }
  78. }
  79. return ;
  80. }
 
 
 
 
 
 
 
 

P2464 [SDOI2008]郁闷的小J的更多相关文章

  1. 洛谷P2464 [SDOI2008] 郁闷的小j [分块]

    题目传送门 郁闷的小j 题目描述 小J是国家图书馆的一位图书管理员,他的工作是管理一个巨大的书架.虽然他很能吃苦耐劳,但是由于这个书架十分巨大,所以他的工作效率总是很低,以致他面临着被解雇的危险,这也 ...

  2. 2018.09.26 洛谷P2464 [SDOI2008]郁闷的小J(map+vector)

    传送门 本来出题人出出来想考数据结构的. 但是我们拥有map+vector/set这样优秀的STL,因此直接用map离散化,vector存下标在里面二分找答案就行了. 代码: #include< ...

  3. 洛谷P2464 [SDOJ2008]郁闷的小J

    洛谷P2464 [SDOJ2008]郁闷的小J 题目描述 小J是国家图书馆的一位图书管理员,他的工作是管理一个巨大的书架.虽然他很能吃苦耐劳,但是由于这个书架十分巨大,所以他的工作效率总是很低,以致他 ...

  4. [SDOI2008]郁闷的小J(分块)

    [SDOI2008]郁闷的小J 题目描述 小J是国家图书馆的一位图书管理员,他的工作是管理一个巨大的书架.虽然他很能吃苦耐劳,但是由于这个书架十分巨大,所以他的工作效率总是很低,以致他面临着被解雇的危 ...

  5. fhqtreap - Luogu 2464 [SDOI2008]郁闷的小J

    [SDOI2008]郁闷的小JJ 题目描述 小J是国家图书馆的一位图书管理员,他的工作是管理一个巨大的书架.虽然他很能吃苦耐劳,但是由于这个书架十分巨大,所以他的工作效率总是很低,以致他面临着被解雇的 ...

  6. 【洛谷 P2464】[SDOI2008]郁闷的小J(线段树)

    题目链接 这题我很久之前用分块写过,没写出来.. 今天又看到了,于是下决心把这题做出来. 这次我用线段树写的,直接对每本书的编号Hash一下然后离散化然后各建一棵线段树,维护当前编号在某个位置有没有书 ...

  7. 山东省选 郁闷的小J

    小J是国家图书馆的一位图书管理员,他的工作是管理一个巨大的书架.虽然他很能吃苦耐劳,但是由于这个书架十分巨大,所以他的工作效率总是很低,以致他面临着被解雇的危险,这也正是他所郁闷的. 具体说来,书架由 ...

  8. 【山东省选2008】郁闷的小J 平衡树Treap

    小J是国家图书馆的一位图书管理员,他的工作是管理一个巨大的书架.虽然他很能吃苦耐劳,但是由于这个书架十分巨大,所以他的工作效率总是很低,以致他面临着被解雇的危险,这也正是他所郁闷的.具体说来,书架由N ...

  9. HUST-1407 郁闷的小J

    离线做法:分别处理每个编号上的各种询问和操作,接着就能用树状数组维护. #include <cstdlib> #include <cstdio> #include <cs ...

随机推荐

  1. Zephyr的Power Management

    1 关于Zephyr Zephyr是Linux基金会维护的微内核项目,来源于WindRiver向Zephyr捐赠的Rocket RTOS内核.主要用于开发针对物联网设备的实时操作系统. Zephyr操 ...

  2. [03] mapper.xml的基本元素概述

    1.select 我们基于这个持久层接口 GirlDao: public interface GirlDao { List<Girl> findByAge(int age); Girl f ...

  3. 从0移植uboot (一) _配置分析

    来源:Linux社区  作者:xiaojiang1025  :http://www.linuxidc.com/Linux/2017-02/141018.htm 和绝大多数源码编译安装一样,uboot的 ...

  4. C# yield关键词使用

    C#有一个关键词yield,简化遍历操作实现的语法糖. 下面Insus.NET使用例子来说明,就拿昨晚的一篇<从字符串数组中把数字的元素找出来> http://www.cnblogs.co ...

  5. 从github checkout子文件夹

    1.将远程项目加载到指定目录:$git init; $git remote add -f origin url2.使用SparseCheckout模式:$git config core.sparsec ...

  6. [Spark][Python]DataFrame select 操作例子

    [Spark][Python]DataFrame中取出有限个记录的例子 的 继续 In [4]: peopleDF.select("age")Out[4]: DataFrame[a ...

  7. [spark][python]Spark map 处理

    map 就是对一个RDD的各个元素都施加处理,得到一个新的RDD 的过程 [training@localhost ~]$ cat names.txtYear,First Name,County,Sex ...

  8. STM32串口打印输出乱码的解决办法

    前言 最近在试用uFUN开发板,下载配套的Demo程序,串口数据输出正常,当使用另一个模板工程,调用串口printf调试功能时,输出的却是乱码,最后发现是外部晶振频率不一样.很多STM32开发板都是使 ...

  9. windows下pwd、ls、tail-f命令使用

    一.问题 习惯了linux命令,在windows上使用cmd没有这些命令时很不习惯. 二.解决办法 2.1 找到这些命令对应的windows命令 ls,对应于windows的dir pwd,对应于wi ...

  10. Haproxy和Nginx负载均衡测试效果对比记录

    为了对比Hproxy和Nginx负载均衡的效果,分别在测试机上(以下实验都是在单机上测试的,即负载机器和后端机器都在一台机器上)做了这两个负载均衡环境,并各自抓包分析.下面说下这两种负载均衡环境下抓包 ...