1. 程式人生 > >BZOJ 2724 淺談分塊演算法求區間眾數

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 */

嗯,就是這樣