【题解】P4168 [Violet]蒲公英
题目大意
求不带修改的区间众数,多次询问强制在线。
Solution
这题是个分块。\(a_i \leq 1e9\)。离散化之后就可以开桶,一开始我想的是对每一块开一个桶,并维护每一块的最小众数。但是这样没法做。没法确定答案就在所有整块的众数和不完整块中所有数构成的集合中,啊就是,这个大小为\(n/block + block\)量级的集合仍然确定不了答案的范围。就,没用!
看了题解之后,发现,我完全可以维护一段块区间的信息。具体的:
- 用\(maxnum[i][j]\)表示闭区间第\(i\)块到第\(j\)块的最小众数。
- 用\(bkt[i][j]\)表示前\(i\)个块\(j\)这个数出现的次数。
有了前缀和\(bkt\)我们就能求出任意块区间的\(bkt\),而求出整个\(bkt[][]\)数组的时间复杂度是\(O(\sqrt{n} * (n + \sqrt{n})) = O(n\sqrt{n})\)的,第一个根号枚举块,然后整个把上一个块的信息复制过来,再根号(块长是根号)把当前快入桶。
\(maxnum[i][j]\)的维护也是\(O(n\sqrt{n})\)的,因为\(maxnum[i][j]\)是由\(maxnum[i][j-1]\)合并上第\(j\)个块的信息得到的,所以除去根号枚举\(i\),后面所有\(j\)的操作只需要\(O(n)\),同时写代码时要记得赋初值\(max[i][j]=max[i][j-1]\)。只需要对每一个\(i\)开一个桶即可。
对于每一个询问\(l[l,r]\),区间内部的整块的众数和区间两端不完整块里的数这些数组成一个集合,答案可以确定在这个集合中。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int rd(){
int res = 0, fl = 1; char c = getchar();
while(!isdigit(c)){if(c == '-') fl = -1; c = getchar();}
while(isdigit(c)){res = (res << 3) + (res << 1) + c - '0'; c = getchar();}
return res * fl;
}
const int maxn = 40010;
int n, nn, m, a[maxn], b[maxn], bl[maxn], block, scc, l, r, B[maxn], bkt[410][maxn], maxnum[410][410], elseblock[maxn], elsemaxnum;
void PreWork(){
//第i个块到第j个块的最小众数 maxnum[i][j]
for(int i(1); i <= scc; ++i){
memset(B, 0, sizeof(B));
for(int j(i); j <= scc; ++j){
maxnum[i][j] = maxnum[i][j - 1];
for(int k(max(1, (j - 1) * block)); k < min(n + 1, j * block); ++k){
B[a[k]]++;
if(((B[a[k]] == B[maxnum[i][j]]) && (a[k] < maxnum[i][j])) || (B[a[k]] > B[maxnum[i][j]])) maxnum[i][j] = a[k];
}
}
}
//前i个块每个数出现的次数
for(int i(1); i <= scc; ++i){
for(int j(1); j <= nn; ++j) bkt[i][j] = bkt[i - 1][j];
for(int j(max(1, (i - 1) * block)); j < min(n + 1, i * block); ++j) bkt[i][a[j]]++;
}
}
int main(){
n = rd(); m = rd();
for(int i = 1; i <= n; ++i) a[i] = rd();
memcpy(b + 1, a + 1, sizeof(int) * n);
sort(b + 1, b + 1 + n);
nn = unique(b + 1, b + 1 + n) - (b + 1);
int Num1 = 39881273, Num2 = 470660633, App1(0), App2(0);
for(int i = 1; i <= n; ++i){
a[i] = lower_bound(b + 1, b + 1 + nn, a[i]) - b;
if(Num1 == b[a[i]]) Num1 = a[i];
if(Num2 == b[a[i]]) Num2 = a[i];
}
block = sqrt(n);
scc = n / block + 1;
for(int i(1); i <= n; ++i) bl[i] = i / block + 1;
PreWork();
while(m--){
l = rd(); r = rd();
l = (l + b[elsemaxnum] - 1) % n + 1; r = (r + b[elsemaxnum] - 1) % n + 1;
if(l > r) swap(l, r);
elsemaxnum = 0;
if(bl[l] + 1 >= bl[r]){
for(int i(l); i <= r; ++i) elseblock[a[i]]++;
for(int i(l); i <= r; ++i) if((elseblock[a[i]] > elseblock[elsemaxnum]) || (elseblock[a[i]] == elseblock[elsemaxnum] && a[i] < elsemaxnum)) elsemaxnum = a[i];
printf("%d\n", b[elsemaxnum]);
for(int i(l); i <= r; ++i) elseblock[a[i]]--;
continue;
}
elsemaxnum = maxnum[bl[l] + 1][bl[r] - 1];
for(int i(l); i <= r && bl[i] == bl[l]; ++i)
elseblock[a[i]]++;
for(int i((bl[r] - 1) * block); i <= r; ++i)
elseblock[a[i]]++;
for(int i(l); i <= r && bl[i] == bl[l]; ++i)
if(((bkt[bl[r] - 1][a[i]] - bkt[bl[l]][a[i]] + elseblock[a[i]]) > (bkt[bl[r] - 1][elsemaxnum] - bkt[bl[l]][elsemaxnum] + elseblock[elsemaxnum])) || (((bkt[bl[r] - 1][a[i]] - bkt[bl[l]][a[i]] + elseblock[a[i]]) == (bkt[bl[r] - 1][elsemaxnum] - bkt[bl[l]][elsemaxnum] + elseblock[elsemaxnum])) && (a[i] < elsemaxnum))) elsemaxnum = a[i];
for(int i((bl[r] - 1) * block); i <= r; ++i)
if(((bkt[bl[r] - 1][a[i]] - bkt[bl[l]][a[i]] + elseblock[a[i]]) > (bkt[bl[r] - 1][elsemaxnum] - bkt[bl[l]][elsemaxnum] + elseblock[elsemaxnum])) || (((bkt[bl[r] - 1][a[i]] - bkt[bl[l]][a[i]] + elseblock[a[i]]) == (bkt[bl[r] - 1][elsemaxnum] - bkt[bl[l]][elsemaxnum] + elseblock[elsemaxnum])) && (a[i] < elsemaxnum))) elsemaxnum = a[i];
for(int i(l); i <= r && bl[i] == bl[l]; ++i)
elseblock[a[i]]--;
for(int i((bl[r] - 1) * block); i <= r; ++i)
elseblock[a[i]]--;
printf("%d\n", b[elsemaxnum]);
}
return 0;
}