zoukankan      html  css  js  c++  java
  • CodeForces

    题意:N个数,M个查询,求[Li,Ri]区间内出现次数等于其数值大小的数的个数。

    分析:用莫队处理离线问题是一种解决方案。但ai的范围可达到1e9,所以需要离散化预处理。每次区间向外扩的更新的过程中,检查该位置的数ai的出现次数是否已经达到ai或ai+1,以判断是否要更新结果。同理,区间收缩的时候判断ai出现次数是否达到ai或ai-1。

    另一种更高效的方法是使用树状数组离线处理查询。用一个vector数组维护每个ai以此出现的位置。显然ai>N的数不会对结果做出贡献,所以数组开1e5就足够了。树状数组的维护操作:从1道N递推,当ai的出现次数sz>=ai后,对其从右往左数的第ai次出现的位置+1;当出现次数sz>ai次后,需要对从右往左数第ai+1的位置减2;但是出现次数sz>ai+1次后,上述操作会多减去一部分,那么相应的就应该在从右往左数第ai+2次出现的位置上+1。

    莫队代码:

    #include<iostream>
    #include<stdio.h>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<map>
    using namespace std;
    const int maxn=1e5+5;
    typedef long long LL;
    int N,M,res;
    struct Node{
        int val;
        int id;
        bool operator < (const Node &p) const {return val<p.val;}
    }a[maxn];
    bool cmpid(const Node &x,const Node &y) {return x.id<y.id;}
    
    int b[maxn];
    int pos[maxn],cnt[maxn],block;                  //块数
    int ans[maxn];
    int v[maxn];            //离散化
    struct Query{
        int L,R,id;
    }Q[maxn];
    bool cmp1(const Query& x,const Query& y){       //根据所属块的大小排序
        if(pos[x.L]==pos[y.L]) return x.R<y.R;
        return pos[x.L]<pos[y.L];
    }  
    
    void add(int pos)
    {
        int id = v[a[pos].id];
        if(cnt[id]==b[pos]-1) res++;
        else if(cnt[id]==b[pos]) res--;
        cnt[id]++;    
    }
    
    void pop(int pos)
    {
        int id = v[a[pos].id];
        if(cnt[id]==b[pos]) res--;
        else if(cnt[id]==b[pos]+1) res++;
        cnt[id]--;
    }
    
    //#define LOCAL
    int main()
    {
        #ifdef LOCAL
            freopen("in.txt","r",stdin);
            freopen("out.txt","w",stdout);
        #endif
        int T;
        int cas=1;
        while(scanf("%d%d",&N,&M)==2){
            block = ceil(sqrt(1.0*N));
            memset(cnt,0,sizeof(cnt));
            for(int i=1;i<=N;++i){
                scanf("%d",&a[i].val);
                a[i].id = i;
                b[i] = a[i].val;
                pos[i]=i/block;
            }
            //离散化
            sort(a+1,a+N+1);
            int tag = 1;
            v[a[1].id] = tag;
            for(int i=2;i<=N;++i){
                if(a[i].val == a[i-1].val) v[a[i].id] = tag;
                else v[a[i].id] = ++tag; 
            }
            sort(a+1,a+N+1,cmpid);
    
            for(int i=1;i<=M;++i){
                scanf("%d%d",&Q[i].L,&Q[i].R);
                Q[i].id = i;
            }
            sort(Q+1,Q+M+1,cmp1);
            res=0;
            int curL=1,curR=0;
            for(int i=1;i<=M;++i){
                while(curL>Q[i].L) add(--curL);
                while(curR<Q[i].R) add(++curR);
                while(curL<Q[i].L) pop(curL++);
                while(curR>Q[i].R) pop(curR--);
                ans[Q[i].id] = res;
            }            
            for(int i=1;i<=M;++i)
                printf("%d
    ",ans[i]);
        }
        return 0;
    }

    离线树状数组代码:

    #include<iostream>
    #include<stdio.h>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<map>
    #include<vector>
    using namespace std;
    const int maxn=1e5+5;
    typedef long long LL;
    int N,M;
    int a[maxn];
    int ans[maxn];
    struct Query{
        int L,R,id;
        bool operator < (const Query &q) const {return R<q.R;}
    }Q[maxn];
    
    int bit[maxn];
    inline int lowbit(int x) {return x&(-x);}
    
    void add(int i,int val){
        for(;i<=N;i+=lowbit(i)) bit[i]+=val;
    }
    
    int sum(int i){
        int res=0;
        for(;i>0;i-=lowbit(i)) res+=bit[i];
        return res;
    }
    
    #define LOCAL
    int main()
    {
        #ifdef LOCAL
            freopen("in.txt","r",stdin);
            freopen("out.txt","w",stdout);
        #endif
        int T;
        int cas=1;
        while(scanf("%d%d",&N,&M)==2){
            vector<int> pos[maxn];
            memset(bit,0,sizeof(bit));
            for(int i=1;i<=N;++i){
                scanf("%d",&a[i]);
            }
            
            for(int i=1;i<=M;++i){
                scanf("%d%d",&Q[i].L,&Q[i].R);
                Q[i].id = i;
            }
            sort(Q+1,Q+M+1);
    
            int la=1;
            for(int i=1;i<=N;++i){
                if(a[i]<=N){                        //如果a[i]>N 那么不可能对结果有贡献
                    pos[a[i]].push_back(i);         //记录出现的位置
                    int sz = pos[a[i]].size();
                    if(sz>=a[i]){                     
                        add(pos[a[i]][sz-a[i]],1);                  //对从右往左数的第a[i]次出现的位置,加1
                        //若a[i]出现的次数大于a[i],从右往左数出现的第a[i]+1次的位置已经被加1,不能作出贡献的前缀被多加了2,所以减去2
                        if(sz>a[i]) add(pos[a[i]][sz-a[i]-1],-2);
                        //但是若a[i]出现的次数大于a[i]+1,那么之前的-2操作就需要“补偿回来”
                        if(sz>a[i]+1) add(pos[a[i]][sz-a[i]-2],1);
                    }
                }
                while(la<=M && Q[la].R==i){
                    ans[Q[la].id] = sum(Q[la].R) - sum(Q[la].L-1);
                    la++;
                }
                if(la>M) break;
            }
            for(int i=1;i<=M;++i)
                printf("%d
    ",ans[i]);
        }
        return 0;
    }
    为了更好的明天
  • 相关阅读:
    c++拷贝构造函数和赋值运算符
    c++运算符定义为成员函数还是非成员函数
    c++重载运算符位置的限制
    为什么operator<<运算符重载一定要为友元函数
    动态规划求一定数量骰子和的概率
    vector之reserve的坑
    c++ decltype和auto对比学习
    asio的前摄器模式
    动态显示当前时间
    js遍历二维数组
  • 原文地址:https://www.cnblogs.com/xiuwenli/p/9340039.html
Copyright © 2011-2022 走看看