1. 程式人生 > >2018牛客網暑假多校第一場J(樹狀陣列+思維)

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\n",sum[i]);
        }
    }
}