BZOJ 2724 淺談分塊演算法求區間眾數
世界真的很大
分塊演算法大概算是一種線上的資料結構,和線段樹作用差不多,但是卻能維護一些線段樹維護不了的資訊。
線段樹要求維護的資訊必須要具備可合併性,就是說子區間的資訊能夠合併到大區間裡,比如區間和,區間值域等
但是分塊卻不需要這樣的條件
有點暴力的味道但是確確實實是一種有效的資料結構
看題先:
description:
input
修正一下
l = (l_0 + x - 1) mod n + 1, r = (r_0 + x - 1) mod n + 1
output
題目也是有點迷
分塊這種資料結構,和線段樹等遞迴的結構不同,旨在將原序列分成若干“塊”,操作的時候如果橫跨整個塊就給整個塊打標記,單獨在某個塊的就暴力操作,以此來節約時間複雜度
如果橫跨很多塊,那麼時間複雜度就是O(塊的數目),對每個塊的操作的時間複雜度就是O(塊的大小),每次操作就是O(塊的數目)+O(塊的大小)的時間複雜度,而已知一共有n個節點,即O(塊的大小)*O(塊的數目)=n
所以由基本(均值)不等式得O(塊的大小)+O(塊的數目)>=2*(O(塊的大小)*O(塊的數目))^0.5
當且僅當O(塊的大小)=O(塊的數目)時,O(塊的大小)+O(塊的數目),即每次操作的時間複雜度最小,而O(塊的大小)*O(塊的數目)=n,所以塊的數目和大小都應該是n^0.5
所以說每次操作的時間複雜度是n^0.5的,總時間複雜度應該是n^1.5
對於這道題來講,對於一個大區間內的眾數,在大區間包含的整塊區間裡的眾數,大區間的眾數要麼是他,要麼是兩段不是整塊的部分的數使得其數量超過了整塊的眾數
所以眾數要麼是整塊區域的眾數,要麼是兩端獨立部分出現的數
查詢時就比較獨立部分,O(n^0.5),和整塊眾數誰出現的多就好,時間複雜度也是可以接受的
以此推得我們需要預處理的資訊,
1.整塊的區域的眾數,即任意塊i到任意塊j的眾數
2.任意數在任意區間出現了多少次
對於1,我們可以暴力列舉每個塊,然後暴力算出其到之後所有塊的眾數,用個桶來處理就好,時間複雜度是(n^l.5)
對於2,我們可以對於每個數的值建一個vector,把這個數出現的位置壓進去,每次用upperbound和lowerbound判斷vector裡L,到R範圍內有多少個數就行了
還有一些細節
資料範圍可能比較大,所以還是離散化一下比較好,記得記錄一下離散之後的值和原值的對應關係
lowerbound和upperbound返回的值是其查詢到的資料結構的位置資訊,而對於vector,這樣的動態資料結構,其返回的是一個迭代器的位置,由於vector元素之間,位置是連續的,所以可以直接相減判斷之間有多少個元素
完整程式碼:
#include<stdio.h>
#include<vector>
#include<math.h>
#include<cstring>
#include<algorithm>
using namespace std;
struct A
{
int sum,idx;
}aa[100010];
vector <int> src[100010];
int pos[100010],rnk[100010],wa[100010],f[1010][1010],b[100010];
int n,m,blk,ans=0,big=0,x=0;
bool cmp(const A &a, const A &b)
{
return a.sum<b.sum;
}
bool cmp2(const A &a, const A &b)
{
return a.idx<b.idx;
}
void init(int x)
{
int ans=0,big=0;
memset(wa,0,sizeof(wa));
for(int i=(x-1)*blk+1;i<=n;i++)
{
int t=pos[i];
wa[aa[i].sum]++;
if(wa[aa[i].sum]>big) big=wa[aa[i].sum],ans=aa[i].sum;
else if(wa[aa[i].sum]==big && aa[i].sum<ans) ans=aa[i].sum;
f[x][t]=ans;
}
}
int Saber(int lf,int rg,int a)
{
vector<int>::iterator x=upper_bound(src[a].begin(),src[a].end(),rg);
vector<int>::iterator y=lower_bound(src[a].begin(),src[a].end(),lf);
return x-y;
}
int query(int L,int R)
{
int ans=f[pos[L]+1][pos[R]-1];
int big=Saber(L,R,ans);
for(int i=L;i<=min(R,pos[L]*blk);i++)
{
int tmp=Saber(L,R,aa[i].sum);
if(tmp>big) big=tmp,ans=aa[i].sum;
else if(tmp==big && aa[i].sum<ans) ans=aa[i].sum;
}
if(pos[L]!=pos[R])
for(int i=(pos[R]-1)*blk+1;i<=R;i++)
{
int tmp=Saber(L,R,aa[i].sum);
if(tmp>big) big=tmp,ans=aa[i].sum;
else if(tmp==big && aa[i].sum<ans) ans=aa[i].sum;
}
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
blk=(int) sqrt(n) ;
for(int i=1;i<=n;i++)
scanf("%d",&aa[i].sum),aa[i].idx=i;
sort(aa+1,aa+n+1,cmp);
int tot=0;
for(int i=1;i<=n;i++)
{
if(aa[i].sum!=aa[i-1].sum)
tot++;
b[i]=tot;
rnk[tot]=aa[i].sum;
}
for(int i=1;i<=n;i++) aa[i].sum=b[i];
sort(aa+1,aa+n+1,cmp2);
for(int i=1;i<=n;i++) src[aa[i].sum].push_back(i);
for(int i=1;i<=n;i++) pos[i]=(i-1)/blk+1;
for(int i=1;i<=pos[n];i++) init(i);
for(int i=1;i<=m;i++)
{
int L,R;
scanf("%d%d",&L,&R);
L=(L+x-1)%n+1,R=(R+x-1)%n+1;
if(L>R) swap(L,R);
x=rnk[query(L,R)];
printf("%d\n",x);
}
return 0;
}
/*
Whoso pulleth out this sword from this stone and anvil is duly born King of all England
*/
嗯,就是這樣