2018牛客網暑假多校第一場J(樹狀陣列+思維)
阿新 • • 發佈:2019-02-08
題目描述:
有一個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]); } } }