这题网上貌似还没有完整的题解呢,我来口胡一下~

Description

\(W \times H\) 的二维坐标系,\(W, H \le 10^9\)

给 \(n (n \le 10^5)\) 个点 \((x, y)\),每个点有个方向,每 \(1\) 时刻箭头朝其位置移动一个单位,如果点相遇就会消失。问点经过轨迹的格子并。

Solution

整体框架分析

如果我们能快速确定每个苦无运动的轨迹(最终在何时刻消失),之后的问题是矩形面积并,用扫描线 \(O(n \log n)\) 解决。

首先我们可以发现,如果不考虑苦无的消失,两个苦无相撞的充要条件即在一条横线、竖线或斜线(呈 \(45°\) 角)上,然后满足一些方向限制,比如,左右相撞的苦无 \(i, j\) 要满足在同一条横线 \(X_i = Y_i\),然后 \(Y_i < Y_j, D_i = 0, D_j = 2\)。

令我们比较困扰的事,可能两个苦无满足上述条件,但在此之前其中有苦无已经消失了,所以这次相遇就失败了。

我们可以按照时间顺序处理,每次把冲突时间最短的若干苦无都拿出来然后让他们消失,如果我们遇到的相遇事件中又苦无已经消失了,就跳过。

快速维护 & 算出最短相撞的苦无

由于每个点分别在一条横线、竖线、斜线上,所以我们可以把每条这样的线及方向当成一个集合,每个点至多在四个集合中。

然后我们现在的问题就变成了:一个数轴上,每个位置可能有两种种类的点 (\(0, 1\)),然后要求最近匹配的 \(01\) 点对(方向不能反),还要支持删除点。

如果我们考虑每个点匹配的最优点,我们是可以通过 \(\text{set}\) 在 \(\log\) 时间算出前驱后继,但删除一个点的时候,可能有多个点的最有匹配是他,要修改多个点,这样就挂了。不妨考虑双向匹配,我们维护 \(x, y\) 互相都作为对方的最优匹配这样的点对,这样是对的,因为单向匹配总能找到更优的双向匹配。所以在删除的一个点的时候,如果他在双向匹配里,让他的匹配点找新的匹配,即可。

这样时间复杂度就是 \(O(n \log n)\)

接下来的工作就是把双向匹配放进一个堆里,每次把所有时间最短的点拿出来删除就行。

一些细节

  1. 关于横竖直线,距离为奇数点的相撞时间问题。比如相邻的两个 → ←,如果右箭头上方有一个下箭头(如题面右下角的图),那个箭头不会相撞,说明相撞时间 \(> 1\),但如果相撞时间又得 $ < 2$,否则会和左箭头相撞,因此这种的相撞时间应该是 \(1.5\)。我是把时间整体 \(\times 2\),这样就可以没有小数。
  2. 一定要每次把所有同一时间的点一块删除,不然可能会存在同一时间,但后者认为前者已经消失的恐怖方法。

时间复杂度

\(O(n \log n)\)

Code

感觉自己确实写的很麻烦,其实独自开个结构体维护那个数轴可能会更简单一点。

#include <iostream>
#include <cstdio>
#include <set>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std; typedef long long LL; const int N = 100005, INF = 2e9; int W, H, n, X[N], Y[N], D[N], L[N][4], f[N], m, a[N << 1], len, d[4][N], tot[4];
int nxt[4][4], pre[4][4], match[N][4], val[N][4], stk[N], top;
LL ans;
bool st[N], re[N]; struct O {
int v, x, y;
bool operator < (const O &b) const { return v > b.v; }
}; struct Node{
int x, id;
bool operator < (const Node &b) const { return x < b.x; }
}; typedef set<Node>::iterator SIT; set<Node> s[4][N][4];
// 第一维:横 / 竖 / 左下 - 右上 / 左上 / 右下; 第二维:离散后线编号; 第三维:存储的方向值
priority_queue<O> q; int inline get(int x, int o) { return lower_bound(d[o] + 1, d[o] + 1 + tot[o], x) - d[o]; } void inline find(int c, int i) {
match[i][c] = 0;
int x = L[i][c], k = c == 0 ? Y[i] : X[i];
if (nxt[c][D[i]] != -1) {
int cq = nxt[c][D[i]];
SIT it = s[c][x][cq].lower_bound( (Node) { k, 0 } );
if (it != s[c][x][cq].end()) {
int j = it -> id, w = (it -> x - k + 1) * 2;
if (c < 2) w = w / 2 + 1;
if (!match[j][c] || w < val[j][c]) {
match[i][c] = j, match[j][c] = i, val[i][c] = val[j][c] = w;
q.push((O){ val[i][c], i, j });
}
}
}
if (pre[c][D[i]] != -1) {
int cq = pre[c][D[i]];
SIT it = s[c][x][cq].lower_bound( (Node) { k, 0 } );
if (it != s[c][x][cq].begin()) {
--it; int j = it -> id, w = (k - it -> x + 1) * 2;
if (c < 2) w = w / 2 + 1;
if (!match[j][c] || w < val[j][c]) {
match[i][c] = j, match[j][c] = i, val[i][c] = val[j][c] = w;
q.push((O){ val[i][c], i, j });
}
}
}
} void inline insert(int c, int i) {
int x = L[i][c], k = c == 0 ? Y[i] : X[i];
s[c][x][D[i]].insert((Node) { k, i } );
find(c, i);
} void inline init() {
memset(nxt, -1, sizeof nxt);
memset(pre, -1, sizeof pre);
nxt[0][0] = 2, pre[0][2] = 0, nxt[1][3] = 1, pre[1][1] = 3;
pre[2][0] = 3, nxt[2][3] = 0, pre[2][1] = 2, nxt[2][2] = 1;
nxt[3][0] = 1, pre[3][1] = 0, nxt[3][3] = 2, pre[3][2] = 3;
for (int i = 1; i <= n; i++) {
d[0][++tot[0]] = X[i], d[1][++tot[1]] = Y[i];
d[2][++tot[2]] = X[i] + Y[i], d[3][++tot[3]] = X[i] - Y[i];
}
for (int i = 0; i < 4; i++) {
sort(d[i] + 1, d[i] + 1 + tot[i]);
tot[i] = unique(d[i] + 1, d[i] + 1 + tot[i]) - d[i] - 1;
}
for (int i = 1; i <= n; i++) {
L[i][0] = get(X[i], 0), L[i][1] = get(Y[i], 1);
L[i][2] = get(X[i] + Y[i], 2), L[i][3] = get(X[i] - Y[i], 3);
for (int j = 0; j < 4; j++) insert(j, i);
}
} void inline del(int i) {
if (re[i]) return;
re[i] = true;
for (int c = 0; c < 4; c++) {
int x = L[i][c], k = c == 0 ? Y[i] : X[i];
s[c][x][D[i]].erase((Node) { k, i } );
if (match[i][c]) find(max(nxt[c][D[i]], pre[c][D[i]]), match[i][c]);
}
} void inline clear() { while (!q.empty() && (st[q.top().x] || st[q.top().y])) q.pop(); } void inline work() {
while (!q.empty()) {
clear();
if (q.empty()) break;
int v = q.top().v;
while (1) {
clear();
if (q.empty() || q.top().v != v) break;
O u = q.top();
f[u.x] = f[u.y] = v >> 1, del(u.x), del(u.y);
stk[++top] = u.x, stk[++top] = u.y; q.pop();
}
while (top) st[stk[top--]] = true;
}
} struct E{
int x, l, r, c;
bool operator < (const E &b) const { return x < b.x; }
} e[N << 1]; void inline add(int x1, int y1, int x2, int y2) {
e[++m] = (E) { x1, y1, y2 + 1, 1 };
e[++m] = (E) { x2 + 1, y1, y2 + 1, -1 };
a[++len] = y2 + 1, a[++len] = y1;
} int inline getY(int y) {
return lower_bound(a + 1, a + 1 + len, y) - a;
} struct T{
int len, cnt;
} t[N * 8]; void inline pushup(int p, int l, int r) {
t[p].len = t[p].cnt ? a[r + 1] - a[l] : t[p << 1].len + t[p << 1 | 1].len;
} void change(int p, int l, int r, int x, int y, int k) {
if (x <= l && r <= y) {
t[p].cnt += k;
if (l == r) t[p].len = t[p].cnt ? a[r + 1] - a[r] : 0;
else pushup(p, l, r);
return;
}
int mid = (l + r) >> 1;
if (x <= mid) change(p << 1, l, mid, x, y, k);
if (mid < y) change(p << 1 | 1, mid + 1, r, x, y, k);
pushup(p, l, r);
} void inline fill() {
for (int i = 1; i <= n; i++) {
if (D[i] == 0) add(X[i], Y[i], X[i], Y[i] + f[i] - 1);
else if (D[i] == 1) add(X[i] - f[i] + 1, Y[i], X[i], Y[i]);
else if (D[i] == 2) add(X[i], Y[i] - f[i] + 1, X[i], Y[i]);
else add(X[i], Y[i], X[i] + f[i] - 1, Y[i]);
}
sort(e + 1, e + 1 + m);
sort(a + 1, a + 1 + len);
len = unique(a + 1, a + 1 + len) - a - 1;
for (int i = 1; i <= m; i++) {
change(1, 1, len, getY(e[i].l), getY(e[i].r) - 1, e[i].c);
if (i != m && e[i].x != e[i + 1].x)
ans += (e[i + 1].x - e[i].x) * (LL)t[1].len;
}
} int main() {
scanf("%d%d%d", &W, &H, &n);
for (int i = 1; i <= n; i++) {
scanf("%d%d%d", Y + i, X + i, D + i);
if (D[i] == 0) f[i] = W - Y[i] + 1;
else if (D[i] == 1) f[i] = X[i];
else if (D[i] == 2) f[i] = Y[i];
else f[i] = H - X[i] + 1;
}
init();
work();
fill();
printf("%lld\n", ans);
return 0;
}

APIO2012 苦无 Kunai的更多相关文章

  1. BZOJ 2810 [Apio2012]kunai

    Orz Starria 现在看来,也不是很难,能做...就是不能写 可以想到维护每个苦无扫过的矩形,然后做矩形面积并即可. 然后发现自己只会$n^2$的处理方法... 想了好久之后问了一发 Starr ...

  2. [APIO2012]

    来自FallDream的博客,未经允许,请勿转载,谢谢. --------------------------------------------------- A.dispatching 派遣 上次 ...

  3. MFC消息映射与命令传递

    题外话:刚开始学视窗程序设计的时候,我就打印了一本Windows消息详解,里面列举了各种已定义消息的意义和作用,共10多页,在编程的时候翻翻,有时觉得很受用.我发觉很多编程的朋友,虽然每天都面对消息, ...

  4. C++中内存分配详解

    转载自51CTO.com           http://developer.51cto.com/art/201107/276154.htm 我们都知道,内存基本上分为静态存储区.堆区和栈区三大部分 ...

  5. 剖析MFC六大关键技术(五六)--消息映射与命令传递

    说到消息,在MFC中,“最熟悉的神秘”可算是消息映射,那是我们刚开始接触MFC时就要面对的东西.有过SDK编程经验的朋友转到MFC编程的时候,一下子觉得什么都变了样.特别是窗口消息及对消息的处理跟以前 ...

  6. MFC六大核心机制之五、六:消息映射和命令传递

    作为C++程序员,我们总是希望自己程序的所有代码都是自己写出来的,如果使用了其他的一些库,也总是千方百计想弄清楚其中的类和函数的原理,否则就会感觉不踏实.所以,我们对于在进行MFC视窗程序设计时经常要 ...

  7. C++ 内存解析

    一.内存基本构成可编程内存在基本上分为这样的几大部分:静态存储区.堆区和栈区.他们的功能不同,对他们使用方式也就不同. 静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在 ...

  8. python中多线程

    多线程 什么是多线程 开启线程的两种方式 进程和线程的区别 Thread对象的其他属性和方法 守护线程 死锁现象与递归锁 信号量.Event定时器 线程Queue 进程池和线程池 什么是多线程 在传统 ...

  9. 【转】C中的静态存储区和动态存储区

    一.内存基本构成    可编程内存在基本上分为这样的几大部分:静态存储区.堆区和栈区.他们的功能不同,对他们使用方式也就不同.    静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个 ...

随机推荐

  1. Mon失效处理方法

    假设环境为三个mon,主机名为mon1.mon2.mon3,现在mon3上面的系统盘损坏,mon的数据完全丢失,现在需要对mon3进行恢复处理 1.停止所有mon进程,可以不停其他进程,需要停止内核客 ...

  2. 查看 /var/log目录下文件个数 命令tree 、cut

    查看 /var/log目录下文件个数 方法1. [root@oldboy learn_shell]# tree -L 1 /var/log/ |tail -1 5 directories, 42 fi ...

  3. python+selenium+chromedriver抓取shodan搜索结果

    作用:免积分抓取shodan的搜索结果,并把IP保存为txt 前提: ①shodan会员(ps:黑色星期五打折) ②安装有python27 ③谷歌浏览器(ps:版本一定要跟chromedriver匹配 ...

  4. web安全入门--入门条件

    介绍:网络安全由习大大提出,是继海.陆.空.外太空的第五大作战领域,也是一个关系国家安全和主权.社会稳定.民族文化继承和发扬的重要问题.其重要性,正随着全球信息化步伐的加快越来越重要.接下来,我向大家 ...

  5. 细说FL Studio中的Wasp合成器功能

    FL Studio 简称FL,因其Logo像水果,故国人亲切的叫他"水果"本章节采用图文结合的方式给大家讲解FL Studio中的Wasp合成器功能.感兴趣的朋友可以一起来交流哦. ...

  6. 使用ABBYY FineReader将文档保存为电子书形式

    运用ABBYY FineReader 15的OCR识别技术,不仅能将PDF文档.图像.扫描页面等保存为可编辑的格式,方便用户的进一步编辑使用:而且还能直接转换为电子书的格式,方便用户使用更为便携的电子 ...

  7. 庐山真面目之四微服务架构Consul和Ocelot简单版本实现

    庐山真面目之四微服务架构Consul和Ocelot简单版本实现 一.简介      在上一篇文章<庐山真面目之三微服务架构Consul简单版本实现>中,我们已经探讨了如何搭建基于Consu ...

  8. robot 如何定义用户关键字、变量

    1.用户关键字,使用robot语法定义的关键字 2.系统关键字  自带的 3.资源文件,自己定义的关键字 4.变量 自己定义的关键字,需单独建一个资源文件 自己写的关键字,需写在***Keywords ...

  9. 【海思】Hi3531A SPI功能的详细配置以及使用

    目录 一.前言 二.SPI管脚信息获取 2.1 SPI_SCLK.SPI_SDI.SPI_SDO管脚复用寄存器 2.2 片选SPI_CSN0-SPI_CSN3管脚寄存器 三.配置和使能与SPI相关的管 ...

  10. Java基础教程——Java简介

    Java是SUN公司1995年推出的一门高级编程语言,是一种面向Internet的编程语言,随着Java技术在Web方面的不断成熟,已经成为Web应用程序的首选开发语言. (2009年SUN公司被Or ...