1 solutions
-
0
洛谷上的题解很多,我这里就浅浅发个按照我思路写的代码吧。自己写的代码总是自认为比较好理解。
鸣谢 @C23SW 帮我找到了两处我抓破头都没发现的bug。
需要特别注意的两点:
- query 第一行那个tmp数组,不能用STL map来替代,因为插入和查询时间复杂度都是 O(log n),还不够优,也不能偷懒用 memset 重置整个数组(还没测过,感觉可能过不了),智能重置查询的那一小段。
- 我的 query 函数返回的是结果在 a 中的序号 x,cout 的是 a[x],但下一次查询需要的值是 a[x],所以 cout 完 a[x] 后要令 x=a[x]。当然,如果你的 query 函数返回的是结果而不是结果的序号,那就不用这一步了。
#include <iostream> #include <cmath> #include <algorithm> using namespace std; // 分块代码的循环太多了,建议搞个宏 #define _for(a, b) for(int i=a; i<=b; i++) #define _for_j(a, b) for(int j=a; j<=b; j++) const int N = 4e4 + 10, SQRT_N = 2e2 + 10; int L[SQRT_N], R[SQRT_N], pos[N]; int n, nblock; // 元素个数,分块的块数 int a[N], t[N]; // 原数组,离散化中间结果数组 int b[N]; // 离散化后的结果(排名) // 离散化 void disc() { sort(t + 1, t + 1 + n); int num = unique(t + 1, t + 1 + n) - (t + 1); _for(1, n) b[i] = lower_bound(t + 1, t + 1 + num, a[i]) - t; } // 初始化分块相关的量 void init() { int t = sqrt(n); // 块的大小 nblock = n / t; // 块数 if (n % t) nblock++; // 求出每一块的左右端点 _for(1, nblock) { L[i] = (i - 1) * t + 1; R[i] = i * t; } // 防止 n%t!=0 时出现错误 R[nblock] = n; // 求出原数组每个元素分在哪一块 _for(1, nblock) _for_j(L[i], R[i]) pos[j] = i; } // 第i块到第j块内的最小众数的编号!编号!编号! int mx[SQRT_N][SQRT_N]; // 从第1块到第i块,(离散化后的)数字j出现的次数 int cnt[SQRT_N][N]; // 预处理分块统计的中间结果 void pre() { _for(1, nblock) { // 要把cnt[i]行的每一个数都复制到下一行 _for_j(1, n) cnt[i][b[j]] = cnt[i - 1][b[j]]; // 然后才是统计出现的数字 _for_j(L[i], R[i]) cnt[i][b[j]]++; // 以上两个过程反过来也可以的,当然要注意用+= } // 统计第i~j块的众数 _for(1, nblock) _for_j(i, nblock) { // val初始化为1表示块i的第一个数出现1次 int idx = L[i], val = 1; // 从第i块的左端点到第j块的右端点 for (int j2 = L[i]; j2 <= R[j]; j2++) { int e = cnt[j][b[j2]] - cnt[i - 1][b[j2]]; if (e > val || (e == val && b[j2] < b[idx])) { val = e; idx = j2; } } mx[i][j] = idx; } } // 暂存统计结果 int tmp[N]; // 返回值:众数的索引 int query(int x, int y) { // 先清空本次查询要用到的桶 _for(x, y) tmp[b[i]] = 0; int p = pos[x], q = pos[y]; if (q - p <= 1) { _for(x, y) tmp[b[i]]++; int key = x; _for(x, y) if ((tmp[b[i]] == tmp[b[key]] && b[i] < b[key]) || tmp[b[i]] > tmp[b[key]]) key = i; return key; } // 左端 _for(x, R[p]) tmp[b[i]]++; // 右端 _for(L[q], y) tmp[b[i]]++; // 对于头、尾两段里出现的每一个数,求出次数最多的那个数 int key = x, maxval = 0; _for(x, R[p]) { int tot = tmp[b[i]] + cnt[q - 1][b[i]] - cnt[p][b[i]]; if ((tot == maxval && b[i] < b[key]) || tot > maxval) key = i, maxval = tot; } _for(L[q], y) { int tot = tmp[b[i]] + cnt[q - 1][b[i]] - cnt[p][b[i]]; if ((tot == maxval && b[i] < b[key]) || tot > maxval) key = i, maxval = tot; } // 再与 [x,y] 的区间众数(加头尾出现次数)进行比较 // 众数(离散化后) int bmax = b[mx[p + 1][q - 1]]; // 众数在 [x,y] 内出现的次数 int tot = tmp[bmax] + cnt[q - 1][bmax] - cnt[p][bmax]; if ((maxval == tot && bmax < b[key]) || maxval < tot) key = mx[p + 1][q - 1]; return key; } int main() { ios::sync_with_stdio(0); cin.tie(0); int m; cin >> n >> m; _for(1, n) cin >> a[i], t[i] = a[i]; disc(); init(); pre(); int x = 0; while (m--) { int l0, r0, l1, r1, l2, r2; cin >> l0 >> r0; l1 = (l0 + x - 1) % n + 1; r1 = (r0 + x - 1) % n + 1; l2 = min(l1, r1), r2 = max(l1, r1); // 调试用 // l2 = l0, r2 = r0; // 查询区间众数的索引 x = query(l2, r2); cout << (x = a[x]) << '\n'; } return 0; }
- 1
Information
- ID
- 284
- Time
- 1000ms
- Memory
- 512MiB
- Difficulty
- 9
- Tags
- # Submissions
- 37
- Accepted
- 2
- Uploaded By