JOISC 2021 Day1T2 IOI Fever
首先考虑固定每个点的方向。
平移坐标系,使 \(1\) 号点为原点,则 \(t\) 时刻 \(1\) 号点可能直接或间接影响的范围是 \(\{(x, y)\mid |x| + |y| \le t\}\)。
注意到不存在重复点,那么可以枚举 \(1\) 号点的方向,并旋转坐标系使 \(1\) 号点向 \(x\) 轴正方向。
那么如果初始方向和影响范围扩大的两个方向之一相同,那么不可能被传染。
进一步地,对于 \(|x| \ne |y|\) 的点,应该朝向垂直于 \(|x|, |y|\) 中较大的一个坐标轴的方向,否则仍然不能进入影响范围。
对于 \(|x| = |y|\) 的点,如果在第一象限或第四象限,那么应该朝垂直 \(x\) 轴方向走,否则应该朝垂直 \(y\) 轴方向走(注意到没有重复点,若向 \(x\) 轴方向则恰好不能被感染)。
两个人能相遇,当且仅当满足一下两种条件之一:
- 两人 \(x\) 或 \(y\) 坐标相同,且初始相向而行;
- 两人在同一条斜线上,即 \(x + y = x' +y'\) 或 \(x - y = x' - y'\),且方向恰好相对。
构造无向图,若 \(u, v\) 两人在 \(t\) 时刻相遇,则在 \((u, v)\) 之间连一条权值为 \(t\) 的边。
考虑贪心。每次选取被感染时间最小的点 \(u\),设其时间为 \(t\),则对于 \(u\) 的所有出边 \(\{(u, v, t') \mid t' \ge t\}\),更新 \(v\) 的最小感染时间 \(t'\)。
直接实现边数 \(\mathcal O(N^2)\)。存在几种优化方法,下面只介绍最容易理解的一种:
对于每条横线、竖线、斜线上的 \(4\) 种方向的点,分别建立线段树。每个点会加入恰好 \(3\) 棵线段树(两条斜线和初始方向所在的横线或竖线)
每次转移相当于对一棵线段树上的一个前缀或后缀的点的值对一条斜率固定的线段取 \(\min\),支持查询全局最小值和删除某个点。
维护每个区间的最左(右)点和最小的能完全覆盖区间的线段,用堆等数据结构维护每个线段树的最小值即可。
如果按值域建立线段树,则空间 \(\mathcal O(N \log V)\),如直接按下标建立,则空间 \(\mathcal O(N)\)。两种方法都可以通过,但空间上常数很大。时间为 \(\mathcal O(N(\log N + \log V))\) 或 \(\mathcal O(N \log N)\)。
实现十分复杂。
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#include <numeric>
#include <vector>
#include <cassert>
using namespace std;
#define LOG(f...) fprintf(stderr, f)
#define all(cont) begin(cont), end(cont)
using ll = long long;
const int N = 100005;
const ll IINF = 2e9;
const ll INF = 1e17;
struct pt { int x, y; };
struct discretizer {
vector<int> v;
void clear() { v.clear(); }
void add(int x) { v.push_back(x); }
void init() { sort(all(v)); v.erase(unique(all(v)), end(v)); }
int operator()(int x) const { return lower_bound(all(v), x) - begin(v); }
};
struct min_seg_tree {
static const int LEN = 1 << 21;
pair<int, pair<int, int>> t[LEN * 2];
void init() { memset((void *)t, 0x3f, sizeof(t)); }
min_seg_tree() { init(); }
void update(int p, ll v, int id) {
if (v > 0x3f3f3f3f) return;
p += LEN;
if (t[p].first <= v) return;
t[p].first = v; t[p].second = {p - LEN, id};
for (p >>= 1; p; p >>= 1)
t[p] = t[p << 1].first < t[p << 1 | 1].first ? t[p << 1] : t[p << 1 | 1];
}
void assign(int p, ll v, int id) {
if (v > 0x3f3f3f3f) return;
p += LEN;
if (t[p].first == v && t[p].second.second == id) return;
t[p].first = v; t[p].second = {p - LEN, id};
for (p >>= 1; p; p >>= 1)
t[p] = t[p << 1].first < t[p << 1 | 1].first ? t[p << 1] : t[p << 1 | 1];
}
void erase(int p) {
p |= LEN; t[p].first = 0x3f3f3f3f;
for (p >>= 1; p; p >>= 1)
t[p] = t[p << 1].first < t[p << 1 | 1].first ? t[p << 1] : t[p << 1 | 1];
}
pair<int, pair<int, int>> top() const { return t[1]; }
};
struct seg_node {
ll b;
pair<ll, int> mn;
int pos, pos_id;
int lc, rc;
} t[N * 32 * 3];
int nc = 0;
int n;
pt p[N];
discretizer dx, dy, d0, d1;
min_seg_tree H;
int idx[N], idy[N], id0[N], id1[N];
int rtx[N][4], rty[N][4], rt0[N][4], rt1[N][4];
template<int k>
struct seg_op {
static const ll pos_init = k < 0 ? -IINF : IINF;
static ll pos_v(int x) { return x ? t[x].pos : pos_init; }
static int new_node() {
++nc;
t[nc].mn = {INF, 0}; t[nc].b = INF; t[nc].pos = pos_init; t[nc].lc = t[nc].rc = 0;
return nc;
}
static void up(int x) {
auto &u = t[x], &lc = t[t[x].lc], &rc = t[t[x].rc];
t[x].mn = min(lc.mn, rc.mn);
if (k > 0) {
if (pos_v(t[x].lc) < pos_v(t[x].rc)) u.pos = pos_v(t[x].lc), u.pos_id = lc.pos_id;
else u.pos = pos_v(t[x].rc), u.pos_id = rc.pos_id;
}
else {
if (pos_v(t[x].lc) > pos_v(t[x].rc)) u.pos = pos_v(t[x].lc), u.pos_id = lc.pos_id;
else u.pos = pos_v(t[x].rc), u.pos_id = rc.pos_id;
}
t[x].mn = min(t[x].mn, make_pair((ll)k * t[x].pos + t[x].b, t[x].pos_id));
}
static void insert(int &x, int l, int r, int p, ll mn, int id) {
if (!x) x = new_node();
if ((k > 0 && p < t[x].pos) || (k < 0 && p > t[x].pos))
t[x].pos = p, t[x].pos_id = id;
t[x].mn = min(t[x].mn, make_pair(mn, id));
if (l == r) return;
int mid = (l + r) >> 1;
p <= mid ? insert(t[x].lc, l, mid, p, mn, id) : insert(t[x].rc, mid + 1, r, p, mn, id);
}
static void erase(int &x, int l, int r, int p) {
if (l == r) { x = 0; return; }
int mid = (l + r) >> 1;
p <= mid ? erase(t[x].lc, l, mid, p) : erase(t[x].rc, mid + 1, r, p);
if (!t[x].lc && !t[x].rc) x = 0;
else up(x);
}
static void update(int &x, int l, int r, int ql, int qr, ll b) {
if (!x || ql > qr) return;
if (ql <= l && r <= qr) {
t[x].b = min(t[x].b, b);
if (t[x].pos != pos_init) t[x].mn = min(t[x].mn, make_pair(t[x].b + t[x].pos * k, t[x].pos_id));
return;
}
int mid = (l + r) >> 1;
if (ql <= mid) update(t[x].lc, l, mid, ql, qr, b);
if (qr > mid) update(t[x].rc, mid + 1, r, ql, qr, b);
up(x);
}
};
int min_x, max_x, min_y, max_y;
int dir[N];
// rt0 : -2 -2 2 2
// rt1 : -2 2 2 -2
void insert_pt(int i) {
int D = dir[i];
ll V = i == 0 ? 0 : INF;
D & 2 ? seg_op<2>::insert(rt0[id0[i]][D], min_x, max_x, p[i].x, V, i)
: seg_op<-2>::insert(rt0[id0[i]][D], min_x, max_x, p[i].x, V, i);
D == 1 || D == 2 ? seg_op<2>::insert(rt1[id1[i]][D], min_x, max_x, p[i].x, V, i)
: seg_op<-2>::insert(rt1[id1[i]][D], min_x, max_x, p[i].x, V, i);
if (D == 0) seg_op<-1>::insert(rty[idy[i]][D], min_x, max_x, p[i].x, V, i);
else if (D == 2) seg_op<1>::insert(rty[idy[i]][D], min_x, max_x, p[i].x, V, i);
else if (D == 1) seg_op<-1>::insert(rtx[idx[i]][D], min_y, max_y, p[i].y, V, i);
else if (D == 3) seg_op<1>::insert(rtx[idx[i]][D], min_y, max_y, p[i].y, V, i);
}
void heap_up(int id, int tid) {
H.update(id, t[tid].mn.first, t[tid].mn.second);
}
void heap_assign(int id, int tid) {
H.assign(id, t[tid].mn.first, t[tid].mn.second);
}
void erase_pt(int i) {
int D = dir[i];
D & 2 ? seg_op<2>::erase(rt0[id0[i]][D], min_x, max_x, p[i].x)
: seg_op<-2>::erase(rt0[id0[i]][D], min_x, max_x, p[i].x);
heap_assign(id0[i] << 4 | 8 | D, rt0[id0[i]][D]);
D == 1 || D == 2 ? seg_op<2>::erase(rt1[id1[i]][D], min_x, max_x, p[i].x)
: seg_op<-2>::erase(rt1[id1[i]][D], min_x, max_x, p[i].x);
heap_assign(id1[i] << 4 | 12 | D, rt1[id1[i]][D]);
if (D == 0) seg_op<-1>::erase(rty[idy[i]][D], min_x, max_x, p[i].x);
else if (D == 2) seg_op<1>::erase(rty[idy[i]][D], min_x, max_x, p[i].x);
else if (D == 1) seg_op<-1>::erase(rtx[idx[i]][D], min_y, max_y, p[i].y);
else if (D == 3) seg_op<1>::erase(rtx[idx[i]][D], min_y, max_y, p[i].y);
if (D == 0 || D == 2)
heap_assign(idy[i] << 4 | 4 | D, rty[idy[i]][D]);
else
heap_assign(idx[i] << 4 | 0 | D, rtx[idx[i]][D]);
}
void update_pt(int i, int dis) {
int slen = (dis + 1) / 2, slen_v = slen * 2;
switch (dir[i]) {
case 0:
seg_op<2>::update(rt0[id0[i]][3], min_x, max_x, p[i].x + slen, max_x, slen_v - 2LL * (p[i].x + slen));
heap_up(id0[i] << 4 | 8 | 3, rt0[id0[i]][3]);
seg_op<2>::update(rt1[id1[i]][1], min_x, max_x, p[i].x + slen, max_x, slen_v - 2LL * (p[i].x + slen));
heap_up(id1[i] << 4 | 12 | 1, rt1[id1[i]][1]);
seg_op<1>::update(rty[idy[i]][2], min_x, max_x, p[i].x + dis, max_x, dis - (p[i].x + dis));
heap_up(idy[i] << 4 | 4 | 2, rty[idy[i]][2]);
break;
case 1:
seg_op<2>::update(rt0[id0[i]][2], min_x, max_x, p[i].x + slen, max_x, slen_v - 2LL * (p[i].x + slen));
heap_up(id0[i] << 4 | 8 | 2, rt0[id0[i]][2]);
seg_op<-2>::update(rt1[id1[i]][0], min_x, max_x, min_x, p[i].x - slen, slen_v + 2LL * (p[i].x - slen));
heap_up(id1[i] << 4 | 12 | 0, rt1[id1[i]][0]);
seg_op<1>::update(rtx[idx[i]][3], min_y, max_y, p[i].y + dis, max_y, dis - (p[i].y + dis));
heap_up(idx[i] << 4 | 0 | 3, rtx[idx[i]][3]);
break;
case 2:
seg_op<-2>::update(rt0[id0[i]][1], min_x, max_x, min_x, p[i].x - slen, slen_v + 2LL * (p[i].x - slen));
heap_up(id0[i] << 4 | 8 | 1, rt0[id0[i]][1]);
seg_op<-2>::update(rt1[id1[i]][3], min_x, max_x, min_x, p[i].x - slen, slen_v + 2LL * (p[i].x - slen));
heap_up(id1[i] << 4 | 12 | 3, rt1[id1[i]][3]);
seg_op<-1>::update(rty[idy[i]][0], min_x, max_x, min_x, p[i].x - dis, dis + (p[i].x - dis));
heap_up(idy[i] << 4 | 4 | 0, rty[idy[i]][0]);
break;
case 3:
seg_op<-2>::update(rt0[id0[i]][0], min_x, max_x, min_x, p[i].x - slen, slen_v + 2LL * (p[i].x - slen));
heap_up(id0[i] << 4 | 8 | 0, rt0[id0[i]][0]);
seg_op<2>::update(rt1[id1[i]][2], min_x, max_x, p[i].x + slen, max_x, slen_v - 2LL * (p[i].x + slen));
heap_up(id1[i] << 4 | 12 | 2, rt1[id1[i]][2]);
seg_op<-1>::update(rtx[idx[i]][1], min_y, max_y, min_y, p[i].y - dis, dis + (p[i].y - dis));
heap_up(idx[i] << 4 | 0 | 1, rtx[idx[i]][1]);
break;
}
}
bool vis[N];
int solve() {
dir[0] = 0;
for (int i = 1; i < n; ++i) {
if (abs(p[i].x) == abs(p[i].y))
dir[i] = p[i].x < 0 ? 0 : (p[i].y > 0 ? 3 : 1);
else if (abs(p[i].x) > abs(p[i].y))
dir[i] = p[i].x > 0 ? 2 : 0;
else
dir[i] = p[i].y > 0 ? 3 : 1;
}
dx.clear(); dy.clear(); d0.clear(); d1.clear();
for (int i = 0; i < n; ++i)
dx.add(p[i].x), dy.add(p[i].y), d0.add(p[i].x - p[i].y), d1.add(p[i].x + p[i].y);
dx.init(); dy.init(); d0.init(); d1.init();
for (int i = 0; i < n; ++i)
tie(idx[i], idy[i], id0[i], id1[i]) = make_tuple(dx(p[i].x), dy(p[i].y), d0(p[i].x - p[i].y), d1(p[i].x + p[i].y));
min_x = IINF; max_x = -IINF;
for (int i = 0; i < n; ++i) {
min_x = min(min_x, p[i].x), max_x = max(max_x, p[i].x);
min_y = min(min_y, p[i].y), max_y = max(max_y, p[i].y);
}
nc = 0;
memset(rtx, 0, sizeof(rtx)); memset(rty, 0, sizeof(rty)); memset(rt0, 0, sizeof(rt0)); memset(rt1, 0, sizeof(rt1));
for (int i = 0; i < n; ++i)
insert_pt(i);
H.update(idy[0] << 4 | 4 | 0, 0, 0);
H.update(id0[0] << 4 | 8 | dir[0], 0, 0);
H.update(id1[0] << 4 | 12 | dir[0], 0, 0);
fill(vis, vis + n, false);
int res = 0;
while (H.top().first != 0x3f3f3f3f) {
int val = H.top().first;
int id = H.top().second.first;
int x = H.top().second.second;
H.erase(id);
if (vis[x]) continue;
erase_pt(x);
vis[x] = true;
++res;
update_pt(x, val);
}
return res;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
scanf("%d", &n);
for (int i = 0; i < n; ++i)
scanf("%d%d", &p[i].x, &p[i].y);
for (int i = 1; i < n; ++i)
p[i].x -= p[0].x, p[i].y -= p[0].y;
p[0].x = 0; p[0].y = 0;
t[0].mn = {INF, 0};
int res = 0;
for (int i = 0; i < 4; ++i) {
res = max(res, solve());
transform(p, p + n, p, [](const pt &p) ->pt { return {-p.y, p.x}; });
}
printf("%d\n", res);
return 0;
}