Loj#6285.數列分塊入門-9-暴力分塊
阿新 • • 發佈:2018-12-31
(有任何問題歡迎留言或私聊 && 歡迎交流討論哦
題目:傳送門
給出一個長為 n 的數列,以及 n 個詢問,詢問區間[L, R]的最小眾數。
思路:
1.分塊暴力搞:
離散化資料
預處理出每兩個塊間的眾數f[i][j]
預處理每個數出現的位置,放在一個桶裡面。
預處理前k塊中第j個數據的出現次數cnt[k][j]
// 其實這個預處理可以不用,也可以每次upper_bound()-lower_bound()代替
一:對於整塊區間直接O(1)查詢眾數f[belong[a]+1][belong[b]-1]
,O(1)查詢數量cnt[belong[b]-1][x]-cnt[belong[a]][x]
。
二:對於旁邊的區間的資料,列舉在查詢區間[a, b]出現的次數。如果次數較大或者資料較小就更新ans。
(頻繁用memset清空1e5的陣列導致跑了6000+ms,改成列舉指定數量快了10倍,577ms左右)
AC程式碼;
//577ms
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<ctime>
#include<vector>
#include<cmath>
#define fuck(x) cout<<"* "<<x<<"\n"
using namespace std;
typedef long long LL;
const int N = 1e5+7;
const int INF = 0x3f3f3f3f;
int n,belong[N],br[N],now[N],cnt[N],f[500][500],l[N],r[N],block,Num,k;
vector<int> g[N];
int ar[N],cw[500][N];
inline void build(){
block=sqrt(n*1.0);
Num=n/block;if (n%block)Num++;
for(int i=1;i<=Num;++i){
l[i]=(i-1)*block+1;r[i]=i*block;
}
r[Num]=n;
for(int i=1;i<=n;++i){
belong[i]=(i-1)/block+1;
now[i]=lower_bound(br+1,br+1+k,ar[i])-br;
cw[belong[i]][now[i]]++;
}
for(int i=1;i<=n;++i){
g[now[i]].push_back(i);
}
for(int i=1;i<=Num;++i){
for(int j=1;j<=k;++j){
cw[i][j]+=cw[i-1][j];
}
}
}
inline int get(int a,int b,int x){
if(belong[a]==belong[b]){
return cw[belong[b]][x]-cw[belong[a]][x];
}
return cw[belong[b]-1][x]-cw[belong[a]][x];
//return (upper_bound(g[x].begin(),g[x].end(),b)-lower_bound(g[x].begin(),g[x].end(),a));
}
inline void chaobaoli(int a,int b){
for(int i=0;i<=k;++i){
cnt[i]=0;
}
int p=f[belong[a]+1][belong[b]-1];
int num_max=get(a,b,p);
int da=min(r[belong[a]],b);
for(int i=a;i<=da;++i){
cnt[now[i]]++;
int tnum=get(a,b,now[i])+cnt[now[i]];
if(tnum>num_max||(tnum==num_max&&br[p]>br[now[i]])){
p=now[i];
num_max=tnum;
}
}
if(belong[a]!=belong[b])
for(int i=l[belong[b]];i<=b;++i){
cnt[now[i]]++;
int tnum=get(a,b,now[i])+cnt[now[i]];
if(tnum>num_max||(tnum==num_max&&br[p]>br[now[i]])){
p=now[i];
num_max=tnum;
}
}
printf("%d\n",br[p] );
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d",&ar[i]);
br[i]=ar[i];
}
sort(br+1,br+1+n);
k=1;
for(int i=2;i<=n;++i){
if(br[i]!=br[i-1]){
br[++k]=br[i];
}
}
build();
for(int i=1;i<=Num;++i){
for(int i=0;i<=k;++i){
cnt[i]=0;
}
int num_max=0,p=0;
for(int j=l[i];j<=n;++j){
cnt[now[j]]++;
if(cnt[now[j]]>num_max||(cnt[now[j]]==num_max&&br[p]>br[now[j]])){
p=now[j];num_max=cnt[now[j]];
}
f[i][belong[j]]=p;
}
}
for(int i=0,a,b;i<n;++i){
scanf("%d%d",&a,&b);
if(a>b)a^=b^=a^=b;
chaobaoli(a,b);
}
return 0;
}
題目描述
給出一個長為 n 的數列,以及 n 個操作,操作涉及詢問區間的最小眾數。
輸入格式
第一行輸入一個數字 n。
第二行輸入 n 個數字,第 i 個數字為 ai,以空格隔開。
接下來輸入 n 行詢問,每行輸入兩個數字 l、r,以空格隔開。
表示查詢位於 [l,r]的數字的眾數。
輸出格式
對於每次詢問,輸出一行一個數字表示答案。
樣例
樣例輸入
4
1 2 2 4
1 2
1 4
2 4
3 4
樣例輸出
1
2
2
2
輸入:
10
1 2 3 4 3 4 5 2 3 4
1 4
2 6
9 10
4 8
1 8
2 10
4 7
3 9
4 8
6 8
輸出:
1
3
3
4
2
3
4
3
4
2