BZOJ3236: [Ahoi2013]作業
阿新 • • 發佈:2018-11-06
由於卡常,這個題變成了許可權題。。。
本蒟蒻表示沒錢氪金。。。
這裡附上洛谷的題面:
題目描述
此時己是凌晨兩點,剛剛做了Codeforces的小A掏出了英語試卷。英語作業其實不算多,一個小時剛好可以做完。然後是一個小時可以做完的數學作業,接下來是分別都是一個小時可以做完的化學,物理,語文......小A壓力巨大。
這是小A碰見了一道非常噁心的數學題,給定了一個長度為n的數列和若干個詢問,每個詢問是關於數列的區間表示數列的第l個數到第r個數),首先你要統計該區間內大於等於a,小於等於b的數的個數,其次是所有大於等於a,小於等於b的,且在該區間中出現過的數值的個數。
小A望著那數萬的資料規模幾乎絕望,只能向大神您求救,請您幫幫他吧。
輸入輸出格式
輸入格式:
第一行n,m
接下來n個數表示數列
接下來m行,每行四個數l,r,a,b
輸出格式:
輸出m行,分別對應每個詢問,輸出兩個數,分別為在l到r這段區間中大小在[a,b]中的數的個數,以及大於等於a,小於等於b的,且在該區間中出現過的數值的個數(具體可以參考樣例)。
輸入輸出樣例
輸入樣例#1: 複製3 4 1 2 2 1 2 1 3 1 2 1 1 1 3 1 3 2 3 2 3
2 2 1 1 3 2 2 1
說明
N<=100000,M<=100000
題解Here!
第一問顯然一個主席樹就沒了。。。
關鍵是第二問。
看到權值的個數,我們想起了區間神器——莫隊!
於是一發莫隊就好了。
但是那個$[a,b]$的限制怎麼辦?
沒事,我們開一個權值線段樹就好。
第一次加入某權值時加個$1$,最後一次刪除某權值時減個$1$就好。
但是線段樹常數太大怎麼辦?
我們可以用權值樹狀陣列代替。
雖然複雜度是$O(n\sqrt n\log_2n)$,但是至少比線段樹常數小。
網上一堆莫隊+分塊然後$O(n\sqrt n)$的演算法。
但是我第一問用主席樹算是為第二問爭取了不小的時間。。。
然後就可以過了。
注意:記得離散化。
當然這個題好像不離散化也可以。
附程式碼:
#include<iostream> #include<algorithm> #include<cstdio> #include<cmath> #define MAXN 100010 using namespace std; int n,m,q,block; int val[MAXN],lsh[MAXN*3],root[MAXN],num[MAXN]; int ans_one[MAXN],ans_two[MAXN]; struct Question{ int l,r,a,b,id; friend bool operator <(const Question &p,const Question &q){ return (p.r/block==q.r/block?(((p.r/block)&1)?p.l>q.l:p.l<q.l):p.r<q.r); } }que[MAXN]; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } namespace BIT{ int bit[MAXN]; inline int lowbit(int x){return x&(-x);} inline void add(int x,int v){for(;x<=n;x+=lowbit(x))bit[x]+=v;} inline int sum(int x){int s=0;for(;x;x-=lowbit(x))s+=bit[x];return s;} } namespace CT{ int size=0; struct Chairman_Tree{ int sum,l,r; }a[MAXN*19]; inline void buildtree(){ root[0]=a[0].sum=a[0].l=a[0].r=0; } void insert(int k,int l,int r,int &rt){ a[++size]=a[rt];rt=size; a[rt].sum++; if(l==r)return; int mid=l+r>>1; if(k<=mid)insert(k,l,mid,a[rt].l); else insert(k,mid+1,r,a[rt].r); } int query(int i,int j,int l,int r,int lside,int rside){ int ans=0; if(l<=lside&&rside<=r)return a[j].sum-a[i].sum; int mid=lside+rside>>1; if(l<=mid)ans+=query(a[i].l,a[j].l,l,r,lside,mid); if(mid<r)ans+=query(a[i].r,a[j].r,l,r,mid+1,rside); return ans; } } inline void add(int x){ if(!num[x])BIT::add(x,1); num[x]++; } inline void del(int x){ num[x]--; if(!num[x])BIT::add(x,-1); } void work(){ int left=1,right=0; for(int i=1;i<=m;i++){ while(left<que[i].l)del(val[left++]); while(left>que[i].l)add(val[--left]); while(right<que[i].r)add(val[++right]); while(right>que[i].r)del(val[right--]); ans_two[que[i].id]=BIT::sum(que[i].b)-BIT::sum(que[i].a-1); } for(int i=1;i<=m;i++)printf("%d %d\n",ans_one[i],ans_two[i]); } void init(){ n=read();m=read(); for(int i=1;i<=n;i++)val[i]=lsh[i]=read(); q=n; CT::buildtree(); block=sqrt(n); for(int i=1;i<=m;i++){ que[i].l=read();que[i].r=read();que[i].a=read();que[i].b=read(); que[i].id=i; lsh[++q]=que[i].a;lsh[++q]=que[i].b; } sort(lsh+1,lsh+q+1); q=unique(lsh+1,lsh+q+1)-lsh-1; for(int i=1;i<=n;i++){ root[i]=root[i-1]; val[i]=lower_bound(lsh+1,lsh+q+1,val[i])-lsh; num[val[i]]=0; CT::insert(val[i],1,q,root[i]); } for(int i=1;i<=m;i++){ que[i].a=lower_bound(lsh+1,lsh+q+1,que[i].a)-lsh; que[i].b=lower_bound(lsh+1,lsh+q+1,que[i].b)-lsh; ans_one[i]=CT::query(root[que[i].l-1],root[que[i].r],que[i].a,que[i].b,1,q); } sort(que+1,que+m+1); } int main(){ init(); work(); return 0; }