BZOJ4241: 历史研究(回滚莫队)
题意
给出$n$个数,每次询问区间$[L, R]$内 每个数*出现次数 的最大值
Sol
回滚莫队,名字真萌qwq
考虑如果用正常莫队的话我们是无法删除的,因为一旦删除了最大元素就无法找到次大元素
这时候有人提出了一种新的计算方式
思想很简单:对于每个询问按照左端点的块的编号进行排序,相同的话按又端点排序
然后暴力计算每个块。
如果询问的两个端点在同一个块中,直接暴力计算,时间复杂度$O(\sqrt{n})$
如果不在同一个块中,这时候右端点是不断递增的,因此暴力计算右端点的复杂度为$O(n)$
但是左端点的位置在块内可是飘忽不定的啊qwq
简单,每次询问之后把左端点移动到所在块的最右段即可,每次计算左端点的复杂度为$O(\sqrt{n})$
因为有$\sqrt{n}$个块,因此总的时间复杂度为$O(\sqrt{n}n)$
#include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #define LL long long using namespace std; const int MAXN = 1e5 + 10, INF = 1e9 + 7; inline int read() { char c = getchar(); int x = 0, f = 1; while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();} while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } int N, Q; LL out[MAXN], ans; int be[MAXN], date[MAXN], cnt[MAXN], a[MAXN], tot, base, num; struct Query { int l, r, id; bool operator < (const Query &rhs) const { return be[l] == be[rhs.l] ? r < rhs.r : be[l] < be[rhs.l]; } }q[MAXN]; LL solve(int l, int r) { static int tim[MAXN]; LL ans = 0; for(int i = l; i <= r; i++) tim[a[i]] = 0; for(int i = l; i <= r; i++) tim[a[i]]++, ans = max(ans, 1ll * tim[a[i]] * date[a[i]]); return ans; } void Add(int x) { cnt[a[x]]++; ans = max(ans, 1ll * cnt[a[x]] * date[a[x]]); } void Del(int x) { cnt[a[x]]--; } int Get(int i, int id) { int R = min(N, id * base), ql = R + 1, qr = ql - 1; ans = 0; memset(cnt, 0, sizeof(cnt)); for(; be[q[i].l] == id; i++) { if(be[q[i].l] == be[q[i].r]) {out[q[i].id] = solve(q[i].l, q[i].r); continue;} while(qr < q[i].r) Add(++qr); LL cur = ans; while(ql > q[i].l) Add(--ql); out[q[i].id] = ans; while(ql < R + 1) Del(ql++);//每次询问完之后重新统计答案 ans = cur; } return i; } main() { //freopen("4241.in", "r", stdin); //freopen("4241.out", "w", stdout); N = read(); Q = read(); base = sqrt(N); for(int i = 1; i <= N; i++) { a[i] = date[i] = read(); be[i] = (i - 1) / base + 1; num = max(num, be[i]); } sort(date + 1, date + N + 1); int tot = unique(date + 1, date + N + 1) - date - 1; for(int i = 1; i <= N; i++) a[i] = lower_bound(date + 1, date + N + 1, a[i]) - date; for(int i = 1; i <= Q; i++) q[i].l = read(), q[i].r = read(), q[i].id = i; sort(q + 1, q + Q + 1); for(int i = 1, id = 1; id <= num; id++) i = Get(i, id); for(int i = 1; i <= Q; i++) printf("%lld\n", out[i]); return 0; } /* 2 3 2 1 2 3 2 2 1 3 3 2 1 2 3 2 2 1 3 */
作者:自为风月马前卒
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。