题目描述

小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:

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

1
1
0
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$,但是本题数据比较水也能过。

  法一代码:

/*Code by 520 -- 10.28*/
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#define il inline
#define ll long long
#define RE register
#define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++)
#define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--)
using namespace std;
using namespace __gnu_pbds;
const int N=;
gp_hash_table<int,int>mp[];
int n,m,a[N],bl[N],ln[N],rn[N],clo,u,v,w;
char opt[]; int gi(){
int a=;char x=getchar();
while(x<''||x>'') x=getchar();
while(x>=''&&x<='') a=(a<<)+(a<<)+(x^),x=getchar();
return a;
} il int query(int x,int y,int z){
int bx=bl[x],by=bl[y],res=;
if(bx==by) {
For(i,x,y) res+=(a[i]==z);
return res;
}
For(i,bx+,by-) res+=mp[i][z];
For(i,x,rn[bx]) res+=(a[i]==z);
For(i,ln[by],y) res+=(a[i]==z);
return res;
} int main(){
n=gi(),m=gi(); clo=sqrt(n);
For(i,,n) {
a[i]=gi(),bl[i]=(i-)/clo+,mp[bl[i]][a[i]]++;
if(!ln[bl[i]]) ln[bl[i]]=i;
rn[bl[i]]=i;
}
For(i,,m){
scanf("%s",opt);
if(opt[]=='Q') u=gi(),v=gi(),w=gi(),printf("%d\n",query(u,v,w));
else {
u=gi(),v=gi();
mp[bl[u]][a[u]]--;
a[u]=v;
mp[bl[u]][a[u]]++;
}
}
return ;
}

  

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

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

  法二代码:  

/*Code by 520 -- 10.28*/
#include<bits/stdc++.h>
#define il inline
#define ll long long
#define RE register
#define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++)
#define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--)
using namespace std;
const int N=;
int n,m,a[N],bl[N],ln[N],rn[N],clo,u,v,w,*q[N<<],cnt;
struct node{
int l,r,x;
}t[N];
short mp[][N<<];
char opt[N][]; int gi(){
int a=;char x=getchar();
while(x<''||x>'') x=getchar();
while(x>=''&&x<='') a=(a<<)+(a<<)+(x^),x=getchar();
return a;
} il bool cmp(int *a,int *b){return *a<*b;} il int query(int x,int y,int z){
int bx=bl[x],by=bl[y],res=;
if(bx==by) {
For(i,x,y) res+=(a[i]==z);
return res;
}
For(i,bx+,by-) res+=mp[i][z];
For(i,x,rn[bx]) res+=(a[i]==z);
For(i,ln[by],y) res+=(a[i]==z);
return res;
} int main(){
n=gi(),m=gi(); clo=sqrt(n);
For(i,,n) {
a[i]=gi(),q[++cnt]=&a[i],bl[i]=(i-)/clo+;
if(!ln[bl[i]]) ln[bl[i]]=i;
rn[bl[i]]=i;
}
For(i,,m){
scanf("%s",opt[i]);
if(opt[i][]=='Q') t[i]=node{gi(),gi(),gi()},q[++cnt]=&t[i].x;
else t[i]=node{gi(),gi(),},q[++cnt]=&t[i].r;
}
sort(q+,q+cnt+,cmp); int lst=-,tot=;
For(i,,cnt) if(*q[i]!=lst) lst=*q[i],*q[i]=++tot; else *q[i]=tot;
For(i,,n) mp[bl[i]][a[i]]++;
For(i,,m){
if(opt[i][]=='Q') printf("%d\n",query(t[i].l,t[i].r,t[i].x));
else {
u=t[i].l,v=t[i].r;
mp[bl[u]][a[u]]--;
a[u]=v;
mp[bl[u]][a[u]]++;
}
}
return ;
}

  

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

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

  法三代码:

/*Code by 520 -- 10.28*/
#include<bits/stdc++.h>
#define il inline
#define ll long long
#define RE register
#define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++)
#define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--)
using namespace std;
const int N=;
int n,m,a[N],*q[N<<],cnt,tot,rt[N],X[N],Y[N],tx,ty;
struct query{
int l,r,x;
}qus[N];
struct node{
int ls,rs,sz;
}t[N*];
char opt[N][]; int gi(){
int a=;char x=getchar();
while(x<''||x>'') x=getchar();
while(x>=''&&x<='') a=(a<<)+(a<<)+(x^),x=getchar();
return a;
} il bool cmp(int *a,int *b){return *a<*b;} void ins(int l,int r,int k,int x,int lst,int &rt){
if(!rt) rt=++tot; t[rt]=t[lst],t[rt].sz+=x;
if(l==r) return;
int m=l+r>>;
if(k<=m) ins(l,m,k,x,t[lst].ls,t[rt].ls);
else ins(m+,r,k,x,t[lst].rs,t[rt].rs);
} il void update(int i,int v){
int k=a[i];
while(i<=n) ins(,cnt,k,v,rt[i],rt[i]),i+=i&-i;
} il int calc(int x){
int l=,r=cnt,k=qus[x].x,res=;
tx=ty=;
for(RE int i=qus[x].l-;i;i-=i&-i) X[++tx]=rt[i];
for(RE int i=qus[x].r;i;i-=i&-i) Y[++ty]=rt[i];
while(){
int mid=l+r>>;
if(l==r) break;
if(mid>=k) {
r=mid;
For(i,,tx) X[i]=t[X[i]].ls;
For(i,,ty) Y[i]=t[Y[i]].ls;
}
else {
l=mid+;
For(i,,tx) X[i]=t[X[i]].rs;
For(i,,ty) Y[i]=t[Y[i]].rs;
}
}
For(i,,ty) res+=t[Y[i]].sz;
For(i,,tx) res-=t[X[i]].sz;
return res;
} int main(){
n=gi(),m=gi();
For(i,,n) a[i]=gi(),q[++cnt]=&a[i];
For(i,,m){
scanf("%s",opt[i]);
if(opt[i][]=='Q') qus[i]=query{gi(),gi(),gi()},q[++cnt]=&qus[i].x;
else qus[i]=query{gi(),gi(),},q[++cnt]=&qus[i].r;
}
sort(q+,q+cnt+,cmp); int lst=-;
For(i,,cnt) if(*q[i]!=lst) lst=*q[i],*q[i]=++tot; else *q[i]=tot;
cnt=tot;
memset(&t[tot=],,sizeof(t[]));
For(i,,n) update(i,);
For(i,,m){
if(opt[i][]=='Q') printf("%d\n",calc(i));
else {
int u=qus[i].l,v=qus[i].r;
update(u,-),a[u]=v,update(u,);
}
}
return ;
}

  法四:平衡树(432ms)

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

  法四代码:

/*Code by 520 -- 10.28*/
#include<bits/stdc++.h>
#define il inline
#define ll long long
#define RE register
#define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++)
#define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--)
using namespace std;
const int N=;
int n,m,a[N],*q[N<<],cnt;
int ch[N][],rt[N],rnd[N],date[N],siz[N];
struct node{
int l,r,x;
}t[N];
char opt[N][]; int gi(){
int a=;char x=getchar();
while(x<''||x>'') x=getchar();
while(x>=''&&x<='') a=(a<<)+(a<<)+(x^),x=getchar();
return a;
} il bool cmp(int *a,int *b){return *a<*b;} il int newnode(int v){
++cnt;
siz[cnt]=,date[cnt]=v,rnd[cnt]=rand();
return cnt;
} il void up(int rt){siz[rt]=siz[ch[rt][]]+siz[ch[rt][]]+;} int merge(int x,int y){
if(!x||!y) return x+y;
if(rnd[x]<rnd[y]) {ch[x][]=merge(ch[x][],y),up(x);return x;}
else {ch[y][]=merge(x,ch[y][]),up(y);return y;}
} void split(int rt,int v,int &x,int &y){
if(!rt) {x=y=;return;}
if(date[rt]<=v) x=rt,split(ch[rt][],v,ch[x][],y),up(x);
else y=rt,split(ch[rt][],v,x,ch[y][]),up(y);
} il void ins(int k,int v){
int x,y; split(rt[k],v,x,y),rt[k]=merge(merge(x,newnode(v)),y);
} il void del(int k,int v){
int x,y,z; split(rt[k],v,x,y),split(x,v-,x,z),rt[k]=merge(x,y);
} int main(){
srand(time());
n=gi(),m=gi();
For(i,,n) a[i]=gi(),q[++cnt]=&a[i];
For(i,,m){
scanf("%s",opt[i]);
if(opt[i][]=='Q') t[i]=node{gi(),gi(),gi()},q[++cnt]=&t[i].x;
else t[i]=node{gi(),gi(),},q[++cnt]=&t[i].r;
}
sort(q+,q+cnt+,cmp); int lst=-,tot=;
For(i,,cnt) if(*q[i]!=lst) lst=*q[i],*q[i]=++tot; else *q[i]=tot;
cnt=;
For(i,,n) ins(a[i],i);
For(i,,m){
if(opt[i][]=='Q') {
int x,y,z;
split(rt[t[i].x],t[i].r,x,y),split(x,t[i].l-,x,z);
printf("%d\n",siz[z]);
rt[t[i].x]=merge(merge(x,z),y);
}
else {
int x,y,z,u=t[i].l,v=t[i].r;
del(a[u],u),a[u]=v,ins(a[u],u);
}
}
return ;
}
 
 
 
 
 
 
 
 

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. Skyline从5.1升级版本到6.5的常见接口变化问题

    1.原来Route对象升级成Presentation对象后,激活方法的变化: 原来5.1版本示例代码: function flyto(thisa) { var thisid = thisa.id; v ...

  2. eclipse 如何引入本地dtd

    一.首先修改xml的打开方式为:XML editor 1.菜单:Window -> Preferences ->General -> Editors -> File  Asso ...

  3. char.IsLetter的使用

    先看一下下面的代码,大家会觉得控制台输出什么? 输出:Chiantxt  .对吗? 因为你看到char.IsLetter这个方法的文字的注释是这样写的: 但实际上输出的结果是这样的: ??? 怎么还输 ...

  4. 『转』MySQL存储过程语法例子

    原文地址:http://blog.chinaunix.net/uid-540802-id-138873.html ------------------------- 自动生成随机数据存储过程 ---- ...

  5. Missing value auth-url required for auth plugin password

    在控制台输入openstack相关命令时提示如下: openstack user list Missing value auth-url required for auth plugin passwo ...

  6. Luogu1514 NOIP2010 引水入城 BFS、贪心

    传送门 NOIP的题目都难以写精简题意 考虑最上面一排的某一个点对最下面一排的影响是什么样的,不难发现必须要是一段连续区间才能够符合题意. 如果不是一段连续区间,意味着中间某一段没有被覆盖的部分比周围 ...

  7. java线程池和中断总结

    目录 java线程池和中断总结 一. 线程池的使用 二. java中断机制 中断的处理 三. 线程间通信机制总结 java线程池和中断总结 本系列文是对自己学习多线程和平时使用过程中的知识梳理,不适合 ...

  8. Luogu P3768 简单的数学题

    非常恶心的一道数学题,推式子推到吐血. 光是\(\gcd\)求和我还是会的,但是多了个\(ij\)是什么鬼东西. \[\sum_{i=1}^n\sum_{j=1}^nij\gcd(i,j)=\sum_ ...

  9. Intel x86_64 Architecture Background 2

    这里是在学习Intel x86_64体系架构时学习到的一些概念,记录下来以供日后参考.如果有错的地方,欢迎指正! CPU上下文切换(context switch): 这个概念第一次听到对我来说是完全陌 ...

  10. Linux查看日志常用命令

    1.动态循环查看文件内容 tail  -n  10  test.log   查询日志尾部最后10行的日志; tail -n +10 test.log    查询10行之后的所有日志; head -n ...