【BZOJ 2724】 蒲公英
【题目链接】
https://www.lydsy.com/JudgeOnline/problem.php?id=2724
【算法】
分块算法在线维护区间众数
分块算法的精髓就在于 : 大段维护,局部朴素
这一题,我们可以将序列分成T段,那么每一段的长度就是(N/T)
对于每个询问,设l处于第p段,r处于第q段,那么 :
若p = q,用朴素算法计算出区间众数即可
否则,将这个序列分为三段 :
1.[L,R[p]] 2. [L[p+1],R[q-1]] 3. [L[q],r]
显然,区间众数只可能是 [L[p+1],R[q-1]]中的众数或[L,R[p]]中的一个数,或[L[q],r]中的一个数
不妨预处理所有以“段边界”为端点每个数出现的次数和区间众数
当T取sqrt3(N)(开三次方)时,时间复杂度是非常优秀的 : O(n^(5/3))
【代码】
#include<bits/stdc++.h> using namespace std; #define MAXN 40010 const int INF = 2e9; int i,j,k,n,m,l,r,l0,r0,lastans,t,block,len; int a[MAXN],val[MAXN],L[42],R[42],pos[MAXN],rk[MAXN]; int cnt[42][42][MAXN],mx[42][42],d[42][42]; inline int query(int l,int r) { int i; static int sum[MAXN]; int mx = 0,ret = 0; int p = pos[l],q = pos[r]; if (p == q) { for (i = l; i <= r; i++) { sum[rk[i]]++; if (sum[rk[i]] > mx || (sum[rk[i]] == mx && rk[i] < ret)) { mx = sum[rk[i]]; ret = rk[i]; } } for (i = l; i <= r; i++) sum[rk[i]]--; return ret; } else { ret = d[p+1][q-1]; mx = cnt[p+1][q-1][ret]; for (i = l; i <= R[p]; i++) { cnt[p+1][q-1][rk[i]]++; if (cnt[p+1][q-1][rk[i]] > mx || (cnt[p+1][q-1][rk[i]] == mx && rk[i] < ret)) { mx = cnt[p+1][q-1][rk[i]]; ret = rk[i]; } } for (i = L[q]; i <= r; i++) { cnt[p+1][q-1][rk[i]]++; if (cnt[p+1][q-1][rk[i]] > mx || (cnt[p+1][q-1][rk[i]] == mx && rk[i] < ret)) { mx = cnt[p+1][q-1][rk[i]]; ret = rk[i]; } } for (i = l; i <= R[p]; i++) cnt[p+1][q-1][rk[i]]--; for (i = L[q]; i <= r; i++) cnt[p+1][q-1][rk[i]]--; return ret; } } int main() { scanf("%d%d",&n,&m); for (i = 1; i <= n; i++) { scanf("%d",&a[i]); val[++len] = a[i]; } sort(val+1,val+len+1); len = unique(val+1,val+len+1) - val - 1; for (i = 1; i <= n; i++) rk[i] = lower_bound(val+1,val+len+1,a[i]) - val; block = (int)pow(n*1.0,1.0/3); if (block) t = n / block; for (i = 1; i <= block; i++) { L[i] = (i - 1) * t + 1; R[i] = i * t; } if (R[block] < n) { block++; L[block] = R[block-1] + 1; R[block] = n; } for (i = 1; i <= block; i++) { for (j = L[i]; j <= R[i]; j++) { pos[j] = i; } } for (i = 1; i <= block; i++) { for (j = i; j <= block; j++) { for (k = L[i]; k <= R[j]; k++) cnt[i][j][rk[k]]++; for (k = 1; k <= len; k++) { if (cnt[i][j][k] > mx[i][j] || (cnt[i][j][k] == mx[i][j] && k < d[i][j])) { mx[i][j] = cnt[i][j][k]; d[i][j] = k; } } } } lastans = 0; for (i = 1; i <= m; i++) { scanf("%d%d",&l0,&r0); l = (l0 + lastans - 1) % n + 1; r = (r0 + lastans - 1) % n + 1; if (l > r) swap(l,r); printf("%d\n",lastans = val[query(l,r)]); } return 0; }