• 2018牛客网暑假多校第一场J(树状数组+思维)


    题目描述:

        有一个n个数的数列,并由q个询问,每一个询问有一个l和r,问你在区间a1—al和ar—an这两个区间中有多少个不同的数。

    题目分析:

        这个题目事实上是spoj的某一题的改编,原题是求l到r区间有多少个不同的数,现在这个题是要求两个分开的区间有多少个不同的数。

        事实上这个题的做法跟求l到r区间有多少个不同的数的做法相类似。首先,因为数据范围很大,因此我们需要进行离线的操作。离线操作我们可以用莫队甚至主席树进行操作。但是这个题我们可以用树状数组进行维护。首先我们先将询问数组以有端点从小到大进行排序,之后,倘若该数为出现过,则在这位上置1;反之,倘若该数未出现过,则利用差分的思想在之前那一位置-1。

        到上面为止,这就是求l到r区间内不同的数的做法。但是在这道题来说,我们要求的是两个独立的区间。因此我们就需要发挥我们的脑洞,我们可以倍增整个数列的大小,此时对于左半个区间,倘若我们将左区间+n,右区间不变,那么我们就将原来两个不相交的区间变为了一个连续的区间。此时,我们就可以用上诉的方法求一段连续的区间的不同的数的个数。

    代码:

    #include <bits/stdc++.h>
    #define maxn 100005*2
    using namespace std;
    int a[maxn];
    int bit[maxn];//树状数组
    int vis[maxn];
    int sum[maxn];
    unsigned int read()//读入挂
    {
        unsigned int ret=0;
        char ch=getchar();
        while(ch>'9'||ch<'0')ch=getchar();
        while(ch>='0'&&ch<='9')
        {
            ret=ret*10+ch-'0';
            ch=getchar();
        }
        return ret;
    }
    struct node{
        int l,r,id;
        bool operator <(const node & w)const{//按照右端点排序
            return r<w.r;
        }
    }q[maxn];
    int lowbit(int x){
        return x&(-x);
    }
    void add(int x,int d){
        while(x<maxn){
            bit[x]+=d;
            x+=lowbit(x);
        }
        return;
    }
    int get_sum(int x){
        int res=0;
        while(x){
            res+=bit[x];
            x-=lowbit(x);
        }
        return res;
    }
    int main()
    {
        int n,Q;
        while(~scanf("%d%d",&n,&Q)){
            memset(bit,0,sizeof(bit));
            memset(vis,0,sizeof(vis));
            for(int i=1;i<=n;i++){
                //scanf("%d",&a[i]);
                a[i]=read();
            }
            for(int i=1;i<=n;i++) a[i+n]=a[i];//建立倍增数组
            for(int i=1;i<=Q;i++){
                int tmp=read();
                q[i].r=tmp+n;//此时询问的右端点为原来的左端点+n
                q[i].l=read();//此时询问的左端点为原来右端点
                //scanf("%d%d",&q[i].l,&q[i].r);
                q[i].id=i;
            }
            sort(q+1,q+1+Q);
            int cur=1;
            for(int i=1;i<=2*n&&cur<=Q;i++){
                if(vis[a[i]]){
                    add(vis[a[i]],-1);//出现过,则在前一位置-1
                }
                vis[a[i]]=i;
                add(vis[a[i]],1);//在该位置1
                while(cur<=Q&&q[cur].r<=i){
                    sum[q[cur].id]=get_sum(q[cur].r)-get_sum(q[cur].l-1);
                    cur++;
                }
            }
            for(int i=1;i<=Q;i++){
                printf("%d
    ",sum[i]);
            }
        }
    }
  • 相关阅读:
    asp.net中页面传值的几种经典方法
    关于ASp.NEt方面的好书,不得不看啊!!!
    Qt Creator 窗体控件自适应窗口大小布局
    自己动手打造T9510E EMUIB502新功能
    OpenCV&Qt学习之四——OpenCV 实现人脸检测与相关知识整理
    Qt 中获取本机IP地址
    嵌入式Linux中GPS信息读取与处理
    OpenCV 学习资源整理
    新Outlook邮箱的客户端设置
    Qt 中显示中文
  • 原文地址:https://www.cnblogs.com/Chen-Jr/p/11007268.html
Copyright © 2020-2023  润新知