【SDOI2008】郁闷的小J

【SDOI2008】郁闷的小J

Description

  小J是国家图书馆的一位图书管理员,他的工作是管理一个巨大的书架。虽然他很能吃苦耐劳,但是由于这个书架十分巨大,所以他的工作效率总是很低,以致他面临着被解雇的危险,这也正是他所郁闷的。
  具体说来,书架由N个书位组成,编号从1到N。每个书位放着一本书,每本书有一个特定的编码。
  小J的工作有两类:
  图书馆经常购置新书,而书架任意时刻都是满的,所以只得将某位置的书拿掉并换成新购的书。
  小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
  ……
  你的任务是写一个程序来回答每个顾客的询问。

Input

  第一行两个整数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的书共多少本。

Output

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

Sample Input

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

Sample Output

1
1
0
2


Hint

【数据范围】
  对于40%的数据,1<=N,M<=5000
  对于100%的数据,1<=N,M<=100000
  对于100%的数据,所有出现的书的编码为不大于2147483647的正数。                                                                            


Solution

这道题似乎有显而易见的树套树做法
但是我们有更简单的做法
因为询问时只关心某一种书籍的个数,所以我们可以对每一种编号开一个能够维护区间和的数据结构即可
平衡树显然是可以做的,但是常数只能说差强人意
所以我们想到了线段树,只要动态开点就行了
顺带一提,这个编号的范围较大,需要离散 
#include<iostream>
#include<cstring>
#include<cstdio>
#include<map>
#define mid ((L+R)>>1)
using namespace std;
const int Maxn=200005;
inline int read(){
    char c;int rec=0;
    while((c=getchar())<'0'||c>'9');
    while(c>='0'&&c<='9')rec=rec*10+c-'0',c=getchar();
    return rec;
}
int cnt=0;
int n,m,ind,root[Maxn],c[Maxn];
struct Dynamic_Segment_Tree{int s[2],d;}tree[Maxn<<4];
inline void Infix(int &v,int L,int R,int p,int x){
    if(!v)v=++ind;tree[v].d+=x;if(L==R)return ;
    int f=(p>mid);f?L=mid+1:R=mid;Infix(tree[v].s[f],L,R,p,x);
}
inline int Ask(int v,int L,int R,int l,int r){
    if(!v)return 0;if(L>r||R<l)return 0;
    if(L>=l&&R<=r)return tree[v].d;
    return Ask(tree[v].s[0],L,mid,l,r)+Ask(tree[v].s[1],mid+1,R,l,r);
}
map<int,int> table;
int main(){
	n=read();m=read();char ch;
	for(int i=1;i<=n;i++){c[i]=read();if(!table[c[i]])table[c[i]]=++cnt;}
	for(int i=1;i<=n;i++)Infix(root[table[c[i]]],1,n,i,1);
	while(m--){
	    while((ch=getchar())!='Q'&&ch!='C');
	    int x=read(),y=read();
	    if(ch=='Q')cout<<Ask(root[table[read()]],1,n,x,y)<<'\n';
	    else {
	    	if(!table[y])table[y]=++cnt;
	    	Infix(root[table[c[x]]],1,n,x,-1),Infix(root[table[c[x]=y]],1,n,x,1);
	    }
	}
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值