Brownie Points II 题解

教练布置的扫描线题,但是感觉算不上那种经典的扫描线。


先手选择的是一条垂直于横坐标轴的直线,并且因为这条线必须穿过某个点,所以可以直接枚举这一条直线。

可以先将所有点以横坐标为优先级升序排序,这样我们枚举这条直线的时候就是在做扫描线,可以更方便地统计答案。

我们其实要做的事情是:维护直线两边的点的纵坐标,并且能够查询纵坐标大于(或小于)某个值的点的个数,支持插入和删除点。

你会发现在统计过程中我们是不用考虑直线两边的点的横坐标的,因为这条直线本身就起到了分类的作用,而对于纵坐标的维护本质上就是单点加,区间求和,很多数据结构都能做到,代码实现中是将纵坐标离散化后用树状数组维护的。

具体地,我们分以下步骤来统计:

  1. 使用两个树状数组,一个维护直线左边的点,一个维护直线右边的点,初始时要将所有的点加入到右边的树状数组。
  2. 从左至右地枚举直线,将直线上经过的点都从右边的树状数组中删除。
  3. 枚举后手会选择这个直线上的哪个点并更新答案。
  4. 将直线上经过的点加入左边的树状数组。
  5. 回到步骤二,直到所有的点都被扫描线扫过了。

关于更新答案这一部分,需要注意的是:本题不是博弈论,不存在“后手会为了让自己的分数超过先手,而选择某一个点使得自己的分数超过先手,但是会比自己能拿到的最大分数更劣”这种情况。只需要按照题意简单模拟即可。

应该讲得很详细了,给一份代码:

#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
typedef long long ll;
int n, ans, sz, rg, Stan, Ollie, resS, resO;
set<int> s;
pair<int, int> p[200005];
vector<int> lsh;
struct BIT {
    int c[200005];
    void clear() {fill(c, c + sz + 1, 0);}
    int lowbit(int x) {return x & (-x);}
    void add(int x, int v) {
        while(x <= sz) c[x] += v, x += lowbit(x);
    }
    int ask(int x) {
        int ret = 0;
        while(x) ret += c[x], x -= lowbit(x);
        return ret;
    }
} pre, suf;
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    while(cin >> n) {
        if(!n) break;
        lsh.clear();
        for(int i = 1; i <= n; ++i) {
            cin >> p[i].first >> p[i].second;
            lsh.push_back(p[i].second);
        }
        stable_sort(p + 1, p + 1 + n);
        stable_sort(lsh.begin(), lsh.end());
        lsh.erase(unique(lsh.begin(), lsh.end()), lsh.end());
        sz = lsh.size();
        pre.clear(), suf.clear();
        for(int i = 1; i <= n; ++i) {
            p[i].second = lower_bound(lsh.begin(), lsh.end(), p[i].second) - lsh.begin() + 1;
            suf.add(p[i].second, 1);
        }
        ans = -2147483647;
        for(int i = 1; i <= n; ++i) {
            rg = i;
            while(rg < n && p[rg + 1].first == p[i].first) ++rg;
            for(int j = i; j <= rg; ++j) {
                suf.add(p[j].second, -1);
            }
            Stan = 2147483647, Ollie = -2147483647;
            for(int j = i; j <= rg; ++j) {
                resS = pre.ask(p[j].second - 1) + suf.ask(sz) - suf.ask(p[j].second);
                resO = pre.ask(sz) - pre.ask(p[j].second) + suf.ask(p[j].second - 1);
                if(resO > Ollie) Ollie = resO;
                if(resO == Ollie) Stan = min(Stan, resS);
            }
            if(Stan > ans) {
                ans = Stan;
                s.clear();
            }
            if(Stan == ans) s.insert(Ollie);
            for(int j = i; j <= rg; ++j) {
                pre.add(p[j].second, 1);
            }
            i = rg;
        }
        cout << "Stan: " << ans << "; Ollie:";
        for(set<int>::iterator it = s.begin(); it != s.end(); ++it) cout << " " << *it;
        cout << ";\n";
    }
    return 0;
}
posted @ 2023-12-05 20:43  A_box_of_yogurt  阅读(11)  评论(0编辑  收藏  举报  来源
Document