zoukankan      html  css  js  c++  java
  • P2464 [SDOI2008]郁闷的小J

    题目描述

    小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(nsqrt 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=100005;
    gp_hash_table<int,int>mp[1005];
    int n,m,a[N],bl[N],ln[N],rn[N],clo,u,v,w;
    char opt[2];
    
    int gi(){
        int a=0;char x=getchar();
        while(x<'0'||x>'9') x=getchar();
        while(x>='0'&&x<='9') a=(a<<3)+(a<<1)+(x^48),x=getchar();
        return a;
    }    
    
    il int query(int x,int y,int z){
        int bx=bl[x],by=bl[y],res=0;
        if(bx==by) {
            For(i,x,y) res+=(a[i]==z);
            return res;
        }
        For(i,bx+1,by-1) 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,1,n) {
            a[i]=gi(),bl[i]=(i-1)/clo+1,mp[bl[i]][a[i]]++;
            if(!ln[bl[i]]) ln[bl[i]]=i;
            rn[bl[i]]=i;
        }
        For(i,1,m){
            scanf("%s",opt);
            if(opt[0]=='Q') u=gi(),v=gi(),w=gi(),printf("%d
    ",query(u,v,w));
            else {
                u=gi(),v=gi();
                mp[bl[u]][a[u]]--;
                a[u]=v;
                mp[bl[u]][a[u]]++;
            }
        }
        return 0;
    }

      

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

      我们显然可以用奇技淫巧优化掉法一中的$log(sqrt n)$。只需要离线操作,并对数的值域离散,然后用空间换时间,一种方法是把块数调小,另一种是直接用short类型来开桶(反正一个块内的元素次数不会超过$sqrt n<2^{16}-1$),能卡着空间过。时间复杂度$O(nsqrt 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=100005;
    int n,m,a[N],bl[N],ln[N],rn[N],clo,u,v,w,*q[N<<1],cnt;
    struct node{
        int l,r,x;
    }t[N];
    short mp[318][N<<1];
    char opt[N][2];
    
    int gi(){
        int a=0;char x=getchar();
        while(x<'0'||x>'9') x=getchar();
        while(x>='0'&&x<='9') a=(a<<3)+(a<<1)+(x^48),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=0;
        if(bx==by) {
            For(i,x,y) res+=(a[i]==z);
            return res;
        }
        For(i,bx+1,by-1) 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,1,n) {
            a[i]=gi(),q[++cnt]=&a[i],bl[i]=(i-1)/clo+1;
            if(!ln[bl[i]]) ln[bl[i]]=i;
            rn[bl[i]]=i;
        }
        For(i,1,m){
            scanf("%s",opt[i]);
            if(opt[i][0]=='Q') t[i]=node{gi(),gi(),gi()},q[++cnt]=&t[i].x;
            else t[i]=node{gi(),gi(),0},q[++cnt]=&t[i].r;
        }
        sort(q+1,q+cnt+1,cmp); int lst=-1,tot=0;
        For(i,1,cnt) if(*q[i]!=lst) lst=*q[i],*q[i]=++tot; else *q[i]=tot;
        For(i,1,n) mp[bl[i]][a[i]]++;
        For(i,1,m){
            if(opt[i][0]=='Q') printf("%d
    ",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 0;
    }

      

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

      本题显然是个带修主席树的板子,只需要离线操作并对值域离散,然后就直接板子咯。时间复杂度$O(nlog^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=100005;
    int n,m,a[N],*q[N<<1],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*300];
    char opt[N][2];
    
    int gi(){
        int a=0;char x=getchar();
        while(x<'0'||x>'9') x=getchar();
        while(x>='0'&&x<='9') a=(a<<3)+(a<<1)+(x^48),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>>1;
        if(k<=m) ins(l,m,k,x,t[lst].ls,t[rt].ls);
        else ins(m+1,r,k,x,t[lst].rs,t[rt].rs);
    }
    
    il void update(int i,int v){
        int k=a[i];
        while(i<=n) ins(1,cnt,k,v,rt[i],rt[i]),i+=i&-i;
    }
    
    il int calc(int x){
        int l=1,r=cnt,k=qus[x].x,res=0;
        tx=ty=0;
        for(RE int i=qus[x].l-1;i;i-=i&-i) X[++tx]=rt[i];
        for(RE int i=qus[x].r;i;i-=i&-i) Y[++ty]=rt[i];
        while(1){
            int mid=l+r>>1;
            if(l==r) break;
            if(mid>=k) {
                r=mid;
                For(i,1,tx) X[i]=t[X[i]].ls;
                For(i,1,ty) Y[i]=t[Y[i]].ls;
            }
            else {
                l=mid+1;
                For(i,1,tx) X[i]=t[X[i]].rs;
                For(i,1,ty) Y[i]=t[Y[i]].rs;
            }
        }
        For(i,1,ty) res+=t[Y[i]].sz;
        For(i,1,tx) res-=t[X[i]].sz;
        return res;
    }
    
    int main(){
        n=gi(),m=gi();
        For(i,1,n) a[i]=gi(),q[++cnt]=&a[i];
        For(i,1,m){
            scanf("%s",opt[i]);
            if(opt[i][0]=='Q') qus[i]=query{gi(),gi(),gi()},q[++cnt]=&qus[i].x;
            else qus[i]=query{gi(),gi(),0},q[++cnt]=&qus[i].r;
        }
        sort(q+1,q+cnt+1,cmp); int lst=-1;
        For(i,1,cnt) if(*q[i]!=lst) lst=*q[i],*q[i]=++tot; else *q[i]=tot;
        cnt=tot;
        memset(&t[tot=0],0,sizeof(t[0]));
        For(i,1,n) update(i,1);
        For(i,1,m){
            if(opt[i][0]=='Q') printf("%d
    ",calc(i));
            else {
                int u=qus[i].l,v=qus[i].r;
                update(u,-1),a[u]=v,update(u,1);
            }
        }
        return 0;
    }

      法四:平衡树(432ms

      我们离线操作并对值域离散后,可以直接用无旋treap维护每个值域的下标中序,那么修改就是简单的删除操作,查询也是简单的分离操作。时间复杂度$O(nlog 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=200005;
    int n,m,a[N],*q[N<<1],cnt;
    int ch[N][2],rt[N],rnd[N],date[N],siz[N];
    struct node{
        int l,r,x;
    }t[N];
    char opt[N][2];
    
    int gi(){
        int a=0;char x=getchar();
        while(x<'0'||x>'9') x=getchar();
        while(x>='0'&&x<='9') a=(a<<3)+(a<<1)+(x^48),x=getchar();
        return a;
    }    
    
    il bool cmp(int *a,int *b){return *a<*b;}
    
    il int newnode(int v){
        ++cnt;
        siz[cnt]=1,date[cnt]=v,rnd[cnt]=rand();
        return cnt;
    }
    
    il void up(int rt){siz[rt]=siz[ch[rt][0]]+siz[ch[rt][1]]+1;}
    
    int merge(int x,int y){
        if(!x||!y) return x+y;
        if(rnd[x]<rnd[y]) {ch[x][1]=merge(ch[x][1],y),up(x);return x;}
        else {ch[y][0]=merge(x,ch[y][0]),up(y);return y;}
    }
    
    void split(int rt,int v,int &x,int &y){
        if(!rt) {x=y=0;return;}
        if(date[rt]<=v) x=rt,split(ch[rt][1],v,ch[x][1],y),up(x);
        else y=rt,split(ch[rt][0],v,x,ch[y][0]),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-1,x,z),rt[k]=merge(x,y);
    }
    
    int main(){
        srand(time(0));
        n=gi(),m=gi();
        For(i,1,n) a[i]=gi(),q[++cnt]=&a[i];
        For(i,1,m){
            scanf("%s",opt[i]);
            if(opt[i][0]=='Q') t[i]=node{gi(),gi(),gi()},q[++cnt]=&t[i].x;
            else t[i]=node{gi(),gi(),0},q[++cnt]=&t[i].r;
        }
        sort(q+1,q+cnt+1,cmp); int lst=-1,tot=0;
        For(i,1,cnt) if(*q[i]!=lst) lst=*q[i],*q[i]=++tot; else *q[i]=tot;
        cnt=0;
        For(i,1,n) ins(a[i],i);
        For(i,1,m){
            if(opt[i][0]=='Q') {
                int x,y,z; 
                split(rt[t[i].x],t[i].r,x,y),split(x,t[i].l-1,x,z);
                printf("%d
    ",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 0;
    }
     
  • 相关阅读:
    PDB文件详解
    C++模板常用功能讲解
    Windows下多线程编程(二)
    关于静态库中使用全局变量可能导致的问题
    js中的函数
    js中字符串的加密base64
    列表推导式
    函数和方法的区别
    xshell连不上虚拟机
    网络编程,并行,并发和协程
  • 原文地址:https://www.cnblogs.com/five20/p/9903954.html
Copyright © 2011-2022 走看看