zoukankan      html  css  js  c++  java
  • 看花 :查询区间内不同元素个数-预处理+树状数组

    牛客网-看花 https://www.nowcoder.com/test/question/1d6f8e0e16de49d094d16057c92d49de?pid=17906015&tid=28876854

    小明有一个花园,花园里面一共有m朵花,对于每一朵花,都是不一样的,小明用1~m中的一个整数表示每一朵花。

    他很喜欢去看这些花,有一天他看了n次,并将n次他看花的种类是什么按照时间顺序记录下来。

    记录用a[i]表示,表示第i次他看了a[i]这朵花。

    小红很好奇,她有Q个问题,问[l,r]的时间内,小明一共看了多少朵不同的花儿,小明因为在忙着欣赏他的花儿,所以想请你帮他回答这些问题。


    输入描述:
    输入两个数n,m;(1<=n<=2000,1<=m<=100);分别表示n次看花,m表示一共有m朵花儿。

    接下来输入n个数a[1]~a[n],a[i]表示第i次,小明看的花的种类;

    输入一个数Q(1<=Q<=1000000);表示小红的问题数量。

    输入Q行 每行两个数l,r(1<=l<=r<=n);表示小红想知道在第l次到第r次,小明一共看了多少不同的花儿。

    输出描述:
    一共Q行

    每一行输出一个数 表示小明在[l,r]的时间内看了多少种花。

    输入例子1:
    5 3
    1 2 3 2 2
    3
    1 4
    2 4
    1 5

    输出例子1:
    3
    2
    3

    离线查询的思想。预处理:先将N个数字读取进来,用next[]数组储存每个数字下一次出现的位置,fir[i]布尔数组表示从当前查询的位置到结尾第i个元素是否为第一次出现,那么区间[1,x]的fir[i]的和即为这个区间的数的个数,用一个树状数组即可。再将M次查询读取进来,按查询区间的左端点进行排序,然后开始,遍历M个查询的左端点,将区间以前出现过的数字用next[]数组转移至左端点以后,这样区间[左端点的坐标,x]的fir[i]的和即为这个区间的数的个数。再对这个区间进行查询。时间复杂度:预处理O(N + Mlog(M)) 查询O(Mlog(N)+N)

    原文链接:https://blog.csdn.net/CZWin32768/article/details/47054947

    #include<iostream>
    #include<algorithm>
    #include<math.h>
    #define ll long long
    #define M 0x3f3f3f3f3f
    using namespace std;
    int n,m,q;
    int a[50005],c[50005],ans[2000005],Next[50005],vis[1000005];
    bool is_first[50005];
    //a[]输入的n个数
    //c[]树状数组求和
    //ans[i]第i个查询的结果
    //is_first[]判断第i个数是否出现过
    //vis[]记录第i个数出现的位置
    //Next[i]第i个数下一次要出现的位置
    
    struct node
    {
        int le;
        int ri;
        int pos;
    }p[4000005];
    bool cmp(node x,node y)//按左端点从小到大排序
    {
        return x.le<y.le;
    }
    int lowbit(int x)
    {
        return x&(-x);
    }
    void update(int x,int v)//更新is_first[x]的值,同时更新相应和
    {
        while(x<=n)
        {
            c[x]=c[x]+v;
            x=x+lowbit(x);
        }
    }
    int getsum(int x)
    {
        int sum=0;
        while(x>0)
        {
            sum=sum+c[x];
            x=x-lowbit(x);
        }
        return sum;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        
        for(int i=n;i>=1;i--)//预处理第i个数出现的位置
        {
            if(!vis[a[i]])
            {
                vis[a[i]]=i;//标记出现的位置
                is_first[i]=true;
            }
            else
            {
                Next[i]=vis[a[i]];//数字a[i]下一次要出现的位置
                is_first[Next[i]]=false;
                is_first[i]=true;//更新状态
                vis[a[i]]=i;
            }
        }
        scanf("%d",&q);
        for(int i=1;i<=q;i++)
        {
            scanf("%d%d",&p[i].le,&p[i].ri);
            p[i].pos=i;
        }
        sort(p+1,p+q+1,cmp);
        for(int i=1;i<=n;i++)
        {
            if(is_first[i])//a[i]是第一次出现,维护树状数组(初始化)
                update(i,1);
        }
        int now=p[1].le;
        int k=1;
        for(int i=1;i<=q;i++)
        {
            for(;k<p[i].le;k++)//在[1,le)区间出现过的数置-1,再将这些数转移到在区间[len,n]出现的位置并置1
            {
                if(is_first[k])
                {
                    update(k,-1);
                    is_first[k]=false;
                    if(Next[k])
                    {
                        is_first[Next[k]]=true;
                        update(Next[k],1);
                    }
                }
            }
            k=p[i].le;
            now=p[i].le;
            ans[p[i].pos]=getsum(p[i].ri)-getsum(p[i].le-1);
        }
        for(int i=1;i<=q;i++)
            printf("%d
    ",ans[i]);
        return 0;
    }
  • 相关阅读:
    npm ci命令比npm installer命令快2至10倍
    Liferay 7.1发布啦
    2016/07/05 zend optimizer
    2016/06/16 phpexcel
    2016/06/13 phpexcel 未完待续
    2016/06/10 日历插件 Datepicker
    2016/06/09 ThinkPHP3.2.3使用分页
    2016/06/02 网摘记录 svn 服务器端 客户端 安装使用
    2016/05/27 php上传文件常见问题总结
    2016/05/25 抽象类与API(接口)差别
  • 原文地址:https://www.cnblogs.com/-citywall123/p/11754754.html
Copyright © 2011-2022 走看看