poj-2464 Brownie Points II

考点

  • 扫描线
  • 树状数组

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <algorithm>
#include <cstring>
#include <iostream>
#include <set>
using namespace std;
const int maxn = 2e5 + 50;
int n, ytot, dy[maxn], lb[maxn], rb[maxn];
set<int> st;

int lowbit(int x) { return x & -x; }

void add(int bit[], int x, int v) {
while (x <= n) bit[x] += v, x += lowbit(x);
}

int query(int bit[], int x) {
int res = 0;
while (x > 0) res += bit[x], x -= lowbit(x);
return res;
}

struct node {
int x_, y_;
node(int x = 0, int y = 0) : x_(x), y_(y) {}
bool operator<(node &x) {
if (x.x_ != x_) return x_ < x.x_;
return y_ < x.y_;
}
} arr[maxn];
#define ax(x) arr[x].x_
#define ay(x) arr[x].y_

void init() {
int x, y;
for (int i = 1; i <= n; ++i) {
cin >> x >> y;
arr[i] = node(x, y), dy[++ytot] = y;
}
sort(arr + 1, arr + 1 + n), sort(dy + 1, dy + 1 + ytot);
ytot = unique(dy + 1, dy + 1 + ytot) - 1 - dy;
for (int i = 1; i <= n; ++i) {
y = lower_bound(dy + 1, dy + 1 + ytot, ay(i)) - dy;
ay(i) = y, add(rb, y, 1);
}
}

void clear() {
ytot = 0;
memset(lb, 0, sizeof(lb)), memset(rb, 0, sizeof(rb));
st.clear();
}

void work() {
int ans = INT_MIN, head = 1, tail = 1;
while (head <= n) {
tail = head;
while (tail <= n && ax(tail) == ax(head)) add(rb, ay(tail++), -1);
int Stan = INT_MAX, Ollie = INT_MIN;
for (int i = head; i < tail; ++i) {
int s = query(lb, ay(i) - 1) + query(rb, ytot) - query(rb, ay(i));
int o = query(lb, ytot) - query(lb, ay(i)) + query(rb, ay(i) - 1);
Ollie = max(Ollie, o);
if (o == Ollie) Stan = min(Stan, s);
}
// y轴全部遍历完之后才能和ans比较!
// 如果每个点都和ans比较,岂不是每个点都被视作最大值了吗?逻辑错误!
if (Stan > ans) st.clear(), ans = Stan;
if (Stan == ans) st.insert(Ollie);
for (int i = head; i < tail; ++i) add(lb, ay(i), 1);
head = tail;
}
printf("Stan: %d; Ollie:", ans);
for (set<int>::iterator it = st.begin(); it != st.end(); ++it) {
printf(" %d", *it);
}
puts(";");
}

int main() {
while (cin >> n && n) init(), work(), clear();
return 0;
}

思路

平面有若干个点,Stan必经一点垂直画一条线,作为y轴;Ollie必经一点水平画一条线,作为x轴。

其中y轴和x轴上的点不算,Stan分数是一、三象限的点个数,Ollie分数是二、四象限的点个数。

StanOllie都要最大化自己的分数,求Stan分数最小值的最大以及最大时对应Ollie的分数。


由于没有什么限制条件可以用来答案二分,只能以每个点作为坐标原点来暴力计算。

将所有坐标对x轴排序,那么同一x的坐标就都处在同一扫描线上,即可视作y轴;

随后遍历扫描线上的每一个点,x轴视作经过该点的y坐标。


如何快速求出,对于每个点的四个象限的点个数呢?

每个点都处在不同的扫描线上,而扫描线彼此之间是绝对平行的(垂直x轴);

且总有:

  • 2、3象限的总个数 = 当前y轴左侧的所有扫描线上个数之和
  • 1、4象限的总个数 = 当前y轴右侧的所有扫描线上个数之和
  • 总个数(恒不变) = y轴左侧个数 + y轴轴上个数 + y轴右侧个数

那么令lb为y轴左侧的权值树状数组,rb为y轴右侧的权值树状数组,代表各范围内的总个数

那么各象限的节点个数就了然了,假设当前原点坐标为(x, y),y最大范围为ytot

  • 第一象限:rb中,y+1~ytot的个数 = 1~ytot的总个数 - 1~y的总个数
  • 第二象限:lb中,y+1~ytot的个数 = 1~ytot的总个数 - 1~y的总个数
  • 第三象限:lb中,1~y-1的个数
  • 第四象限:rb中,1~y-1的个数

初始化时,先令rb为全平面的总点数;每遇到一根新的扫描线时,将其从rb中剔除,再进行计算,最后将该扫描线纳入到lb中。这样就能保证在计算时,rblb都不存在扫描线上(y轴)的点。