1. 程式人生 > >bzoj2724: [Violet 6]蒲公英(分塊)

bzoj2724: [Violet 6]蒲公英(分塊)

pro moto 第一步 ++i 出了 for 個數 b+ span

傳送門

md調了一個晚上最後發現竟然是空間開小了……明明算出來夠的……

講真其實我以前不太瞧得起分塊,覺得這種基於暴力的數據結構一點美感都沒有。然而今天做了這道分塊的題才發現分塊的暴力之美(如果我空間沒有開小就更美了)

我們先將整個數組分塊,設塊的大小為$T$

我們先預處理出所有以塊邊界為端點的區間的答案,即$ans[L][R]$代表著第$L$塊到第$R$塊的序列所代表的答案。這個可以$O(n*n/T)$預處理

然後我們先將所有的數給離散化,然後對每一個值都開一個vector,記錄這個值在數組中出現的每一個位置。比如數組的下標為1,3,5的位置都是3,那麽3的vector記錄的就是{1,3,5}

這個有什麽用呢?我們設查詢的區間為$[l,r]$,然後在這個vector裏先二分查找第一個大於等於$l$的數的位置,再二分查找第一個大於$r$的數的位置,那麽兩個位置一減就是這個數在這個區間中的出現次數。比如查詢區間$[2,4]$,我們先找到第一個大於等於2的數3,在vector中下標為2,再找第一個大於4的數為5,下標為3,那麽3-2=1就是3這個數字在這個區間中的出現次數

那麽,我們設$[L,R]$為查詢區間之間的整塊,因為我們第一步已經預處理出了所有塊與塊之間的答案,那麽這一段之間的眾數也就可以知道。那麽,只有區間$[l,L-1]$和$[R+1,r]$之間的數字有可能更新答案。那麽我們就去枚舉這兩個區間中的所有數字,然後用上面說的方法去查詢它在整個查詢區間內的出現次數,然後更新答案即可

復雜度為$O(n*n/T+n*T*logn)$,設塊的大小為$n/sqrt{nlogn}$ ,那麽時間復雜度就是$O(nsqrt{nlogn})$

其實還有一種更快的方法是先預處理出塊與塊之間的答案和各個數的出現次數,然後查詢只要在散塊裏暴力增加並更新答案,之後暴力復原即可(然而我懶並不想打)

然後基本註意點都寫在註解裏了

 1 //minamoto
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<vector>
 7
#include<cmath> 8 #define inf 0x3f3f3f3f 9 using namespace std; 10 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 11 char buf[1<<21],*p1=buf,*p2=buf; 12 inline int read(){ 13 #define num ch-‘0‘ 14 char ch;bool flag=0;int res; 15 while(!isdigit(ch=getc())) 16 (ch==-)&&(flag=true); 17 for(res=num;isdigit(ch=getc());res=res*10+num); 18 (flag)&&(res=-res); 19 #undef num 20 return res; 21 } 22 char sr[1<<21],z[20];int C=-1,Z; 23 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;} 24 inline void print(int x){ 25 if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x; 26 while(z[++Z]=x%10+48,x/=10); 27 while(sr[++C]=z[Z],--Z);sr[++C]=\n; 28 } 29 const int N=40005,M=1005; 30 int ans[M][M],a[N],b[N],cnt[N],rt[N],vis[N]; 31 vector<int> pos[N]; 32 int n,m,q,lastans=0,s,l,r; 33 inline int query_cnt(int x){ 34 //查詢數的出現次數,註意l和r要開全局變量 35 return upper_bound(pos[x].begin(),pos[x].end(),r)-lower_bound(pos[x].begin(),pos[x].end(),l); 36 } 37 void init(){ 38 //暴力枚舉塊與塊之間的答案 39 for(int i=1;i<=rt[n];++i){ 40 memset(cnt,0,sizeof(cnt)); 41 int bg=s*(i-1)+1,res=a[bg]; 42 for(int j=bg;j<=n;++j){ 43 ++cnt[a[j]]; 44 if(cnt[a[j]]>cnt[res]||(cnt[a[j]]==cnt[res]&&a[j]<res)) res=a[j]; 45 ans[i][rt[j]]=res; 46 } 47 } 48 } 49 int query(int l,int r){ 50 //查詢,小塊暴力,大塊直接找答案 51 if(rt[r]-rt[l]<=1){ 52 int id=0,res=0; 53 for(int i=l;i<=r;++i) 54 if(!vis[a[i]]){ 55 int t=query_cnt(a[i]); 56 if(t>res||(t==res&&a[i]<id)) res=t,id=a[i]; 57 vis[a[i]]=1; 58 } 59 for(int i=l;i<=r;++i) vis[a[i]]=0; 60 return b[id]; 61 } 62 int L=rt[l]+1,R=rt[r]-1; 63 int LL=(L-1)*s+1,RR=R*s; 64 int id=ans[L][R],res=query_cnt(id);vis[id]=1; 65 for(int i=l;i<LL;++i) 66 if(!vis[a[i]]){ 67 int t=query_cnt(a[i]); 68 if(t>res||(t==res&&a[i]<id)) res=t,id=a[i]; 69 vis[a[i]]=1; 70 } 71 for(int i=RR+1;i<=r;++i) 72 if(!vis[a[i]]){ 73 int t=query_cnt(a[i]); 74 if(t>res||(t==res&&a[i]<id)) res=t,id=a[i]; 75 vis[a[i]]=1; 76 } 77 for(int i=l;i<LL;++i) vis[a[i]]=0; 78 for(int i=RR+1;i<=r;++i) vis[a[i]]=0; 79 vis[ans[L][R]]=0; 80 return b[id]; 81 } 82 int main(){ 83 n=read(),q=read(),s=sqrt(n/(double)(log2(n))+1); 84 //我怕s會變成0所以sqrt裏加了個1(可能並不需要) 85 for(int i=1;i<=n;++i) a[i]=b[i]=read(),rt[i]=(i-1)/s+1;//分塊 86 sort(b+1,b+1+n),m=unique(b+1,b+1+n)-b-1; 87 for(int i=1;i<=n;++i) a[i]=lower_bound(b+1,b+1+m,a[i])-b,pos[a[i]].push_back(i); 88 //以上是離散 89 init(); 90 while(q--){ 91 l=read(),r=read(); 92 l=(l+lastans-1)%n+1,r=(r+lastans-1)%n+1; 93 if(l>r) swap(l,r); 94 print(lastans=query(l,r)); 95 } 96 Ot(); 97 return 0; 98 }

bzoj2724: [Violet 6]蒲公英(分塊)