題意
給出一個長度為 n n n 的序列,有 m m m 次詢問,每次詢問給出 [ l , r ] [l,r] [l,r],求區間衆數。強制線上。
n , m ≤ 500000 n,m\leq 500000 n,m≤500000。
分析
首先對 a i a_i ai 離散化,現在值域變成了 [ 1 , n ] [1,n] [1,n]。
考慮分塊。
首先,設 f i , j f_{i,j} fi,j 表示第 i i i 塊到第 j j j 塊之間的衆數,這個可以在 O ( n n ) O(n\sqrt{n}) O(nn
) 求出。具體做法是對于每一個塊, O ( n ) O(n) O(n) 掃一遍求出衆數。
然後,詢問的時候,衆數隻可能有兩種情況:
-
衆數不是不完整塊中的數
此時答案就是 f b l l + 1 , b l r − 1 f_{bl_{l}+1,bl_{r}-1} fbll+1,blr−1。
-
衆數是不完整塊中的數
首先設答案初值為 f b l l + 1 , b l r − 1 f_{bl_{l}+1,bl_{r}-1} fbll+1,blr−1,出現次數為 m x mx mx。
然後我們可以 O ( n ) O(\sqrt{n}) O(n
) 暴力枚舉這個數,然後考慮這個數在 [ l , r ] [l,r] [l,r] 出現的次數是不是大于 m x mx mx。怎麼看一個數在 [ l , r ] [l,r] [l,r] 的出現次數呢?我們對每個數開一個 v e c t o r vector vector,存這個數所有出現的位置,然後 u p p e r _ b o u n d upper\_bound upper_bound 減 l o w e r _ b o u n d lower\_bound lower_bound 就行了。這樣複雜度是 O ( n n l o g n ) O(n\sqrt{n}logn) O(nn
logn) 的。
考慮優化,在枚舉左邊的數 a i a_i ai 時,我們可以存儲目前數在 v e c t o r vector vector 裡面出現的位置,記作 p o s i pos_i posi,如果 p o s i + m x pos_i+mx posi+mx 不超過 v e c t o r vector vector 的大小,而且 v a i , p o s i + m x ≤ r v_{a_i,pos_i+mx}\leq r vai,posi+mx≤r,說明 a i a_i ai 在區間 [ l , r ] [l,r] [l,r] 出現了大于 m x mx mx 次,此時可以 m x = m x + 1 mx = mx + 1 mx=mx+1。
由于 m x mx mx 隻會更新不超過 2 n 2\sqrt{n} 2n
次,是以總複雜度為 O ( n n ) O(n\sqrt{n}) O(nn
)。
這裡給出 蒲公英 的代碼。
代碼如下:
#include <bits/stdc++.h>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
LL z = 1;
int ksm(int a, int b, int p){
int s = 1;
while(b){
if(b & 1) s = z * s * a % p;
a = z * a * a % p;
b >>= 1;
}
return s;
}
const int N = 5e5 + 5;
int a[N], b[N], pos[N], B, bl[N], cnt[N], L[710], R[710], f[710][710], tot, n;
vector<int> v[N];
void init(int x){
int ans = 0, mx = 0;
for(int i = L[x]; i <= n; i++){
cnt[a[i]]++;
if(cnt[a[i]] > mx || (cnt[a[i]] == mx && ans > a[i])) mx = cnt[a[i]], ans = a[i];
f[x][bl[i]] = ans;
}//f[i][j] 表示第 i 塊到第 j 塊的衆數
for(int i = L[x]; i <= n; i++) cnt[a[i]]--;
}
int QUERY(int l, int r){
int ans = 0, mx = 0;
for(int i = l; i <= r; i++){
cnt[a[i]]++;
if(cnt[a[i]] > mx || (cnt[a[i]] == mx && ans > a[i])) mx = cnt[a[i]], ans = a[i];
}
for(int i = l; i <= r; i++) cnt[a[i]]--;
return ans;
}
int query(int l, int r){
if(bl[l] == bl[r]) return QUERY(l, r);
int ans = f[bl[l] + 1][bl[r] - 1], mx = upper_bound(v[ans].begin(), v[ans].end(), r) - lower_bound(v[ans].begin(), v[ans].end(), l);//中間的塊的衆數及出現次數
for(int i = l; i <= R[bl[l]]; i++){//左邊不完整塊
if(pos[i] + mx - 1 < v[a[i]].size() && v[a[i]][pos[i] + mx - 1] <= r){
if(a[i] < ans) ans = a[i];
}//如果 a[i] 出現次數大于等于 mx 次且 a[i] 更小,就更新 ans
while(pos[i] + mx < v[a[i]].size() && v[a[i]][pos[i] + mx] <= r) ans = a[i], mx++;//如果大于 mx 次,更新 ans
}
for(int i = L[bl[r]]; i <= r; i++){//右邊不完整塊
if(pos[i] - mx + 1 >= 0 && v[a[i]][pos[i] - mx + 1] >= l){
if(a[i] < ans) ans = a[i];
}
while(pos[i] - mx >= 0 && v[a[i]][pos[i] - mx] >= l) ans = a[i], mx++;
}
return ans;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int q;
cin >> n >> q;
B = sqrt(n);
for(int i = 1; i <= n; i++) cin >> a[i], b[i] = a[i];
sort(b + 1, b + n + 1);
int m = unique(b + 1, b + n + 1) - b - 1;
for(int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + m + 1, a[i]) - b;//離散化
for(int i = 1; i <= n; i++) v[a[i]].push_back(i), pos[i] = v[a[i]].size() - 1;
for(int i = 1; i <= n; i++) bl[i] = (i - 1) / B + 1;
for(int i = 1; i <= n; i++) R[bl[i]] = i;
for(int i = n; i >= 1; i--) L[bl[i]] = i;
tot = (n - 1) / B + 1;//分塊預處理
for(int i = 1; i <= tot; i++) init(i);//預處理 f 數組
int x;
for(int i = 1; i <= q; i++){
int l, r;
cin >> l >> r;
l = (l + x - 1) % n + 1;
r = (r + x - 1) % n + 1;
if(l > r) swap(l, r);
x = b[query(l, r)];
cout << x << "\n";
}
return 0;
}