E 仓鼠与珂朵莉
仓鼠与饭堂
https://ac.nowcoder.com/acm/contest/8925/A
主席树 + 启发式合并思想
题意:
给你一个长度为n的数组, 每次操作选择一个区间 然后问 选择一个数
然就出
再这个区间的次数的乘积。
题解:
如果给你一个区间 对于暴力的求法一般是枚举每个数, 再去查找这个数出现了几次,然后算出最大的贡献。
这样对于每次询问都是 的复杂度。
如果我们每次只找 出现一次 且最大的数, 出现两次且最大的数, 出现三次且最大的数…………
假设已经知道看出现一次且最大的数, 出现两次且最大的数…………
那么枚举的时间复杂度为 大概是
左右,但是这样有个问题?
如何找到出现一次且最大的数, 出现两次且最大数?
我想了很久好像没有可行的方法。
但是, 仔细想一下, 你会发现, 如果最大值出现了 次也就意味着小于
次的就不用再找,那么也就意味着
找 第一个比 最大值小 且 出现次数大于 次的数。
那么这样每次的复杂度就是 (
表示最大值出现的次数
表示第一个比最大小的数出现的次数…………)
每次寻找都比之前的大,是不是启发式合并的复杂度啊, 哦好像是的
那么怎么找到这个每个数出现的次数呢?
这里可以用到主席树了
主席树没法确定单个数字出现的个数只能求出这个区间有多少个数, 当这个区间的总个数都小于 那么肯定没有单点信息大于
, 在维护一个每个数 在
区间出现的 重要程度的最大值, 如果区间重要程度的最大值都小于 直接找的答案也就没用必要再找, 这样两个限制大概率保证了每个数出现的个数了。
所以总共的时间复杂度为
#include<bits/stdc++.h> using namespace std; const int N = 1e5 + 7; typedef long long ll; struct hjt{ int l, r, cnt; ll sum; }tree[40 * N]; vector<ll> g; int get_id(int x) { return lower_bound(g.begin(), g.end(), x) - g.begin() + 1; } ll a[N], n, q, b[N]; int rt[N], top = 1; #define m (l + r) / 2 void update(int pos, int l, int r, int last, int &now) { now = top++; tree[now] = tree[last]; tree[now].cnt++; if (l == r) { tree[now].sum += g[l - 1]; return; } if (pos <= m) update(pos, l, m, tree[now].l, tree[now].l); else update(pos, m + 1, r, tree[last].r, tree[now].r); tree[now].sum = max(tree[tree[now].l].sum, tree[tree[now].r].sum); } ll mx = 0; ll ans = 0; void query(int last, int now, int l, int r) { if (l == r) { ll su = tree[now].cnt - tree[last].cnt; ll va = tree[now].sum - tree[last].sum; mx = max(mx, 1ll*su); ans = max(ans, va); return; } int sumr = tree[tree[now].r].cnt - tree[tree[last].r].cnt; int suml = tree[tree[now].l].cnt - tree[tree[last].l].cnt; if (sumr > mx && ans < tree[tree[now].r].sum) { query(tree[last].r, tree[now].r, m + 1, r); } if (suml > mx && ans < tree[tree[now].l].sum) { query(tree[last].l, tree[now].l, l, m); } } int main() { scanf("%lld %lld", &n, &q); for (int i = 1; i <= n; i++) { scanf("%lld", &a[i]); g.push_back(a[i]); } sort(g.begin(), g.end()); g.erase(unique(g.begin(), g.end())); for (int i = 1; i <= n; i++) { b[i] = get_id(a[i]); } int maxn = g.size(); for (int i = 1; i <= n; i++) { update(b[i], 1, maxn, rt[i - 1], rt[i]); } ll last = 0; while (q--) { ll l, r; scanf("%lld %lld", &l, &r); l = (l ^ last) % n + 1; r = (r ^ last) % n + 1; if (l > r) swap(l, r); ans = 0; mx = 0; query(rt[l - 1], rt[r], 1, maxn); printf("%lld\n", ans); last = ans; } }