[Violet]蒲公英 分块 + 二分查找 详解
题面
解析
前言
一道从黑掉到紫的阴间分块题。。。
显然是一道求静态区间众数题。
因为区间众数不具有可加性,用线段树,树状数组的数据结构并不好维护,再加上本题的数据范围和两秒的限制,所以本题可以分块。
暴力做法
瞎搞能拿70 ~ 80 分,这里就不赘述了。
分块做法
正常分块,不过维护的信息变为从 \(块_i\) ~ \(块_j\) 的众数是哪个,我们用 \(f[i][j]\) 来表示。
假设已经知道这个信息怎么维护了,那么对于每个询问 \(L\) ~ \(R\) ,答案只会有三种情况:
- 中间的连续的块里的众数(\(f[pos[L] + 1][pos[R] - 1]\))
- 左边剩余的数里的众数。(\(pos[i]\) 表示 \(i\) 处于哪一个块)
- 右边剩余的数里的众数。(暴力求)
以上的做法就完全是分块的味道了。
下面我们来说 \(f[i][j]\) 的求法:
其实就是两层 \(for\) 循环暴力枚举。。。
写完交上去于是发现你 \(TLE\) 了,所以我们还要找找哪里能优化。
初始化的 \(f[i][j]\) 是没要优化也不好优化的,发现每次询问里的区间两边的暴力很浪费时间,每次都是 \(O(n)\) 的。所以我们可以用二分查找的方式降低时间复杂度。
用 \(vector\) 存 \(a[i]\) 每次在序列中出现的下标,因为 \(a[i] \leq 1e9\) 所以显然还要离散化。然后进入正题,如何二分查找 \(a[i]\) 在 \(L\) ~ \(R\) 中的出现次数。
对于本题的样例,\(2\) 对应的 \(vector\) 里存的是 {2,4,6},我们要找 \(2\) 在 \(3\) ~ \(6\) 中 出现的次数,找到最后一个小于等于 \(R:6\) 的数 \(6\) ,第一个大于等于 \(L:3\) 的数 \(3\),两者下标相减,以此就能到区间 \(L\) ~ \(R\) 中任意数的出现次数。
代码
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
using namespace std;
const int N = 4e4 + 5;
int a[N],r[N],pos[N],f[1000][1000],cnt[N],n,m,block;
vector<int> g[N];
void init() {
block = max(1,(int)(n / sqrt(m * log2(n))));
for(int i = 1; i <= n; i++)
pos[i] = (i - 1) / block + 1;
}
inline int Get(int l,int r,int val) {
return upper_bound(g[val].begin(),g[val].end(),r) - lower_bound(g[val].begin(),g[val].end(),l);
}
void work(int x) {
memset(cnt,0,sizeof(cnt));
int maxn = -1,ans = 0;
for(int i = (x - 1) * block + 1; i <= n; i++) {
cnt[a[i]]++;
if(cnt[a[i]] > maxn || (cnt[a[i]] == maxn && a[i] < ans)) {
maxn = cnt[a[i]];
ans = a[i];
}
f[x][pos[i]] = ans;
}
}
int query(int l,int r) {
int ans = f[pos[l] + 1][pos[r] - 1],maxn = Get(l,r,ans);
int up = min(r,pos[l] * block);
for(int i = l; i <= up; i++) {
int cnt = Get(l,r,a[i]);
if(cnt > maxn || (cnt == maxn && a[i] < ans)) {
maxn = cnt;
ans = a[i];
}
}
if(pos[l] == pos[r]) return ans;
for(int i = (pos[r] - 1) * block + 1; i <= r; i++) {
int cnt = Get(l,r,a[i]);
if(cnt > maxn || (cnt == maxn && a[i] < ans)) {
maxn = cnt;
ans = a[i];
}
}
return ans;
}
inline int read() {
int x = 0,flag = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')flag = -1;ch = getchar();}
while(ch >='0' && ch <='9'){x = (x << 3) + (x << 1) + ch - 48;ch = getchar();}
return x * flag;
}
int main() {
n = read(),m = read();
for(int i = 1; i <= n; i++) r[i] = a[i] = read();
sort(r + 1,r + 1 + n);
int tot = unique(r + 1,r + 1 + n) - (r + 1);
for(int i = 1; i <= n; i++) {
a[i] = lower_bound(r + 1,r + 1 + tot,a[i]) - r;
g[a[i]].push_back(i);
} init();
for(int i = 1; i <= pos[n]; i++) work(i);
for(int i = 1,x = 0; i <= m; i++) {
int L = read(),R = read();
L = (L + x - 1) % n + 1;
R = (R + x - 1) % n + 1;
if(L > R) swap(L,R);
x = r[query(L,R)]; printf("%d\n",x);
}
return 0;
}

浙公网安备 33010602011771号