【UNR #6 C】稳健型选手(分治)(主席树)(二分)

稳健型选手

题目链接:UNR #6 C

题目大意

有一排卡牌,然后每次询问一个区间,问先手最多的分数。
玩法是先手后手轮流选一张牌拿走,先手任选,后手一定会选最左边的。
然后分数是拿的牌的分数和。

思路

考虑一次询问怎么搞:
不难想到一个反着的贪心,每次把数加进堆里面,每次有偶数个就把最大的拿走。

发现奇数有问题,不过你会发现奇数的话最后一个数一定会去到,所以可以转化成偶数的问题。

其实不难又想到一个前面的贪心:
两个两个看,先拿优的(扔进堆里),然后看劣的会不会之前最劣的优的优,然后换。

然后考虑你有两个方向的贪心,有没有什么搞头。
那就类似扩展,考虑分治,于是考虑解决跨过 mid 的询问。

然后你考虑从 mid 往两边扩展得到两边的贪心,问题是如何合并两边的贪心。
思考一下过程,就是左边一些选了的不选,右边的一些不选的选了。

那我们可以用两个主席树分别记录左边选了的,右边不选的。
(因为要主席树所以记得要离散化)
然后我们二分一个交换数量,使得最优即可。

然后注意到基本上都是两个两个处理,所以要分端点的奇偶来分开搞。
然后就差不多了。

代码

#include<queue> #include<cstdio> #include<vector> #include<iostream> #include<algorithm> #define ll long long using namespace std; const int N = 2e5 + 100; int n, Q, a[N], num, b[N], X[N], Y[N]; //int g[N], b[N]; //priority_queue <pair<ll, int>, vector<pair<ll, int> >, less<pair<ll, int> > > q; vector <int> qq, qu[N]; ll ans[N], pre[N], suf[N]; int rt1[N], rt2[N]; priority_queue <int> q; struct XD_tree { int num[N << 6], ls[N << 6], rs[N << 6], tot; ll sum[N << 6]; int copy(int x) { int now = ++tot; num[now] = num[x]; ls[now] = ls[x]; rs[now] = rs[x]; sum[now] = sum[x]; return now; } void change(int &now, int l, int r, int pl, int val) { now = copy(now); num[now] += val; sum[now] += 1ll * b[pl] * val; if (l == r) return ; int mid = (l + r) >> 1; if (pl <= mid) change(ls[now], l, mid, pl, val); else change(rs[now], mid + 1, r, pl, val); } int ask(int now, int l, int r, int k) { if (l == r) return l; int mid = (l + r) >> 1; if (num[ls[now]] >= k) return ask(ls[now], l, mid, k); else return ask(rs[now], mid + 1, r, k - num[ls[now]]); } ll query(int now, int l, int r, int k) { if (k >= num[now]) return sum[now]; if (k <= 0) return 0; if (l == r) return 1ll * b[l] * k; int mid = (l + r) >> 1; if (num[ls[now]] >= k) return query(ls[now], l, mid, k); else return sum[ls[now]] + query(rs[now], mid + 1, r, k - num[ls[now]]); } void clear(int m) { tot = 0; } }T; void slove(int l, int r, vector <int> p) { if (!p.size()) return ; for (int i = l; i <= r; i++) qu[i].clear(); int mid = (l + r) >> 1; vector <int> lp, rp; lp.clear(); rp.clear(); for (int i = 0; i < p.size(); i++) { int id = p[i]; if (Y[id] <= mid) {lp.push_back(id); continue;} if (mid < X[id]) {rp.push_back(id); continue;} qu[X[id]].push_back(id); } for (int op = 0; op < 2; op++) { int m = mid + op; while (!q.empty()) q.pop(); T.clear(m); pre[m] = suf[m + 1] = 0; rt1[m] = rt2[m + 1] = 0; for (int i = m + 1; i <= r; i++) {//右边(主席树记录空的) rt1[i] = rt1[i - 1]; pre[i] = pre[i - 1]; if ((i - m) % 2 == 0) { int x = i, y = i - 1; if (a[x] > a[y]) swap(x, y); pre[i] += b[a[y]]; q.push(-a[y]); if (!q.empty() && -q.top() < a[x]) { int val = -q.top(); q.pop(); q.push(-a[x]); pre[i] += b[a[x]] - b[val]; T.change(rt1[i], 1, b[0], val, 1); } else T.change(rt1[i], 1, b[0], a[x], 1); } } while (!q.empty()) q.pop(); for (int i = m; i >= l; i--) {//左边(主席树记录有的) rt2[i] = rt2[i + 1]; suf[i] = suf[i + 1]; q.push(a[i]); if ((m - i + 1) % 2 == 0) { int x = q.top(); q.pop(); suf[i] += b[x]; T.change(rt2[i], 1, b[0], x, 1); } } while (!q.empty()) q.pop(); for (int i = m - 1; i >= l; i -= 2) { for (int pp = 0; pp < qu[i].size(); pp++) { int id = qu[i][pp]; int x = X[id], y = Y[id]; int L = 1, R = min((y - m) / 2, (m - x + 1) / 2), re = 0; while (L <= R) { int mid = (L + R) >> 1; if (T.ask(rt1[y], 1, b[0], (y - m) / 2 - mid + 1) >= T.ask(rt2[x], 1, b[0], mid)) re = mid, L = mid + 1; else R = mid - 1; } ll val = suf[x] + pre[y]; val -= T.query(rt2[x], 1, b[0], re); val += T.sum[rt1[y]] - T.query(rt1[y], 1, b[0], (y - m) / 2 - re); ans[id] += val; } } } slove(l, mid, lp); slove(mid + 1, r, rp); } int main() { // freopen("ex_game4.in", "r", stdin); // freopen("write.txt", "w", stdout); scanf("%d %d", &n, &Q); for (int i = 1; i <= n; i++) scanf("%d", &a[i]), b[i] = a[i]; // if ((n <= 500 && Q <= 500) || Q == 1) { // for (int p = 1; p <= Q; p++) { // int l, r; scanf("%d %d", &l, &r); // num = (r - l + 1 + 1) / 2; // for (int i = 1; i <= num; i++) { // if (i == num && (r - l + 1) & 1) { // g[i] = a[r]; b[i] = 0; // } // else { // g[i] = max(a[l + (i - 1) * 2], a[l + (i - 1) * 2 + 1]); // b[i] = min(a[l + (i - 1) * 2], a[l + (i - 1) * 2 + 1]); // } // } // // ll di = g[num]; while (!q.empty()) q.pop(); // q.push(make_pair(b[num], num)); // for (int i = num - 1; i >= 1; i--) { // di += g[i]; // if (q.top().first > g[i]) { // di += q.top().first - g[i]; // q.pop(); // q.push(make_pair(g[i], i)); // } // q.push(make_pair(b[i], i)); // } // printf("%lld\n", di); // } // // return 0; // } sort(b + 1, b + n + 1); b[0] = unique(b + 1, b + n + 1) - b - 1; for (int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + b[0] + 1, a[i]) - b; for (int i = 1; i <= Q; i++) { scanf("%d %d", &X[i], &Y[i]); int x = X[i], y = Y[i]; if ((y - x + 1) & 1) ans[i] = b[a[Y[i]]], Y[i]--, y--; if (x > y) continue; qq.push_back(i); } slove(1, n, qq); for (int i = 1; i <= Q; i++) printf("%lld\n", ans[i]); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/UNR_6_C.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(35)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示