看花 :查詢區間內不一樣元素個數-預處理+樹狀數組

 

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

小明有一個花園,花園裏面一共有m朵花,對於每一朵花,都是不同的,小明用1~m中的一個整數表示每一朵花。ios

他很喜歡去看這些花,有一天他看了n次,並將n次他看花的種類是什麼按照時間順序記錄下來。數組

記錄用a[i]表示,表示第i次他看了a[i]這朵花。spa

小紅很好奇,她有Q個問題,問[l,r]的時間內,小明一共看了多少朵不一樣的花兒,小明由於在忙着欣賞他的花兒,因此想請你幫他回答這些問題。.net


輸入描述:
輸入兩個數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/47054947code

 

#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\n",ans[i]);
    return 0;
}
相關文章
相關標籤/搜索