zoukankan      html  css  js  c++  java
  • 求区间不同数的个数 树状数组||莫队算法

    题目:https://www.nowcoder.com/acm/contest/139/J
    题意:给出n个数,求 [1,L],[R,n]这两个区间不同数的个数
    其实你只要把区间扩大一倍,就是求 [R,L+n]这个区间了

    求区间内不同数的个数解决方法有很多

    像用离线树状数组、离线莫队、线段树、主席树等等

    不过听说主席树会TLE,所以这里主要说一下树状数组和莫队算法
    1.树状数组(BIT)
    其实可以用树状数组就一定能用线段树,因为树状数组就是从线段树中演变来的,
    只不过BIT写起来更方便,其复杂度跟线段树一样,都是 O(log n)
    它主要进行区间求和和区间内某个值的加减
    现在我们回到题目,求一段区间不同数的个数,做法就是先离线按照R从小到大排序,
    然后用map或者一个标记数组,一直只记录每个重复值最后一次出现的下标,用BIT
    在这个位置置1,每次更新就-1,一直维护重复值的最后一个的下标+1,然后每次用

    BIT去求和就行了

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    #define MAX 200005
    int bit[MAX],n,q;
    map<int,int>mp;
    struct Query
    {
        int l,r,id;
        bool operator < (const Query& a)const{
          return r<a.r;
        }
    }b[MAX];
    int gsum(int i)
    {
        int s=0;
        while(i>0)
        {
            s+=bit[i];
            i-=i&-i;
        }
        return s;
    }
    void add(int i,int k)
    {
        while(i<=n)
        {
            bit[i]+=k;
            i+=i&-i;
        }
    }
    int main()
    {
        while(~scanf("%d %d",&n,&q)){
          mp.clear();
        memset(bit,0,sizeof(bit));
        vector<int>a(n*2+10),ans(q);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            a[i+n]=a[i];
        }
        n=n*2;
        for(int i=0;i<q;i++)
        {
            int l,r;
            scanf("%d %d",&l,&r);
            b[i].l=r;
            b[i].r=l+n/2;
            b[i].id=i;
        }
        sort(b,b+q);
        int pre=1;
        for(int i=0;i<q;i++)
        {
            for(int j=pre;j<=b[i].r;j++)
            {
                if(mp[a[j]])
                {
                    add(mp[a[j]],-1);
                }
                add(j,1);
                mp[a[j]]=j;
            }
            pre=b[i].r+1;
          ans[b[i].id]=gsum(b[i].r)-gsum(b[i].l-1);
        }
        for(int i=0;i<q;i++)
        {
            printf("%d
    ",ans[i]);
        }
      }
    }

    2、莫队算法

    莫队算法这个就是从枚举暴力中优化来的,其复杂度是O(n^1.5);

    前提是:如果我们已知[l,r]的答案,能在O(1)时间得到[l+1,r]的答案以及[l,r-1]的答案,

    即可使用莫队算法。

    给几个链接看看:http://www.cnblogs.com/hzf-sbit/p/4056874.html

    http://ydcydcy1.blog.163.com/blog/static/21608904020134411543898/

    其中的精华就是分块,用一个block数组将元素分成根号n块,

    即:for(int i=1;i<=n;i++)

              block[i]=i/sqrt(n);

    然后是排序,在本题中就是:

    bool cmp(const Query&a,const Query&b)
    {
        if(block[a.l]==block[b.l])
            return a.r<b.r;
        else return a.l<b.l;
    }

    最后就是从[L,R]推出[L,R+1]或者[L+1,R]

    完整代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    #define MAX 100005
    int block[MAX],ans[MAX],n,b[MAX];
    int sum=0;
    int read()
    {
        char ch=' ';
        int ans=0;
        while(ch>'9'||ch<'0')
            ch=getchar();
        while(ch>='0'&&ch<='9')
        {
            ans=ans*10+ch-'0';
            ch=getchar();
        }
        return ans;
    }
    struct Query
    {
        int l,r,id;
    }s[MAX];
    bool cmp(const Query&a,const Query&b)
    {
        if(block[a.l]==block[b.l])
            return a.r<b.r;
        else return a.l<b.l;
    }
    void inc(int x)
    {
       if(b[x]==0)sum++;
       b[x]++;
    }
    void dec(int x)
    {
        b[x]--;
        if(b[x]==0)sum--;
    }
    int main()
    {
        int q;
        while(~scanf("%d %d",&n,&q))
        {
            memset(b,0,sizeof(b));
            vector<int>a(n);
            for(int i=1;i<=n;i++)
            {
                 a[i]=read();
                block[i]=i/1000;
            }
            for(int i=1;i<=q;i++)
            {
                s[i].l=read();
                s[i].r=read();
                s[i].id=i;
            }
            sort(s+1,s+q+1,cmp);
            int L=0,R=n+1;
            sum=0;
            for(int i=1;i<=q;i++)
            {
                while(L<s[i].l){L++;inc(a[L]);}
                while(L>s[i].l){dec(a[L]);L--;}
                while(R<s[i].r){dec(a[R]);R++;}
                while(R>s[i].r){R--;inc(a[R]);}
                ans[s[i].id]=sum;
            }
            for(int i=1;i<=q;i++)
                printf("%d
    ",ans[i]);
        }
    }

    这还是牛客网暑假第一场的签到题…………

  • 相关阅读:
    架构师养成记--34.Redis持久化
    架构师养成记--33.Redis哨兵、redis简单事务
    js获取当前项目根路径URL (转自CSDN 红领巾-sunlight)
    架构师养成记--32.Redis高级(安全 主从复制)
    架构师养成记--31.Redis的几种类型
    架构师养成记--30.Redis环境搭建
    oracle split(转自--博客园 linbo.yang)
    架构师养成记--29.redis开篇
    129、Java面向对象之static关键字一(修改static变量)
    128、Java面向对象之对象的比较
  • 原文地址:https://www.cnblogs.com/zhgyki/p/9361822.html
Copyright © 2011-2022 走看看