Codeforces 1401E Divide Square

Description

给定一个 $10^6×10^6$ 的正方形,$n$ 条横线和 $m$ 条竖线穿过了它,求这些线把正方形分成了多少个部分。

注意: 每条线的两个端点之一一定在正方形的边上

Solution

只有两种情况,会产生一个新的块:

1. square 的对边被一条 segment 连接,如图:

2. 两条 segment 相交,如图:

注意,因为每条 segment 必然有一段与 square 的一边相连,则两条相交必会产生一个新的块。

所以,我们可以分这两种情况来计算所有的块。

首先,$ans \gets 1$(没有 segment 也有初始的一个块)。

第一种情况是很好处理的,对于读入的某条横边或者竖边,如果它的两个端点分别为 $0$ 和 $10^6$,则 $ans \gets ans + 1$。

对于第二种情况,我们可以抽象为一个单点修改,区间查询的问题。

比如,我们把横边看作「修改」,竖边看作「查询」。

下面,我们依次处理横坐标位于 $1, 2, \cdots 10^6-1$ 的交点(也就是从左往右扫描),可以抽象为 $1-$时刻,$2-$时刻……,在每一个时刻,先处理查询操作,再处理修改操作

尝试动态维护一个数组 $\text{cover}$,$\text{cover}_i = 0/1$ 表示在当前时刻下,纵坐标 $i$ 上面是否被线段覆盖。

  • 定义操作 $\operatorname{\large{M}\small{ODIFY}}\normalsize{(t, p, v)}$ 表示在 $t$ 时刻,将 $\text{cover}_p$ 增加 $v$;
  • 定义操作 $\operatorname{\large{Q}\small{UERY}}\normalsize{(t, l, r)}$ 表示询问在 $t$ 时刻,查询 $\sum\limits_{i=l}^{r}\text{cover}_i$。

描述一下一条横边 $(y, lx, rx)$,它表示,在时刻 $[lx, rx]$ 中,把 $\text{cover}_y$ 赋值为 $1$。那么,通过差分的方法,我们可以把每一条横边抽象为两个修改操作:$\operatorname{\large{M}\small{ODIFY}}\normalsize{(lx-1, y, 1)}$ 和 $\operatorname{\large{M}\small{ODIFY}}\normalsize{(rx, y, -1)}$。因为是先查询后修改,所以,$lx$ 时刻就应该被计入的贡献应该在 $lx-1$ 时刻修改,同理,$rx$ 时刻以后就要被撤销的影响应该在 $rx$ 时刻修改。

描述一下一条竖边 $(x, ly, ry)$,它会与在 $x$ 时刻,$\text{cover}_i = 1(ly \le i \le ry)$ 的那些纵坐标上的线段相交。那么,总量就是一个查询操作,即 $\operatorname{\large{Q}\small{UERY}}\normalsize{(x, ly, ry)}$,将其累加到 $ans$ 中。

那么,将所有操作按照「时间不同时,按照时间升序;时间相同时,先修改后查询」的方式排序,这就成为了一个标准的单点修改,区间查询的问题,可以用树状数组/线段树解决。

时间复杂度 $\mathcal O(n \log n)$,代码中用的是树状数组,将坐标范围平移到了 $[1, 10^6 + 1]$ 防止越界,并且将修改和查询分开了,当然也可以存在一起排序。

#include <bits/stdc++.h>
#define int long long
#define AddModification(t, p, v) mdfy[++m0] = (modification){t, p, v};
#define AddQuery(t, l, r) qry[++q0] = (query){t, l, r};
using namespace std;
const int N = 1e5 + 5, S = 1e6 + 5;
int y[N], lx[N], rx[N], x[N], ly[N], ry[N];
int ans = 1, n, m, o[S], m0, q0;
struct modification
{
	int t, p, v;
	bool operator < (const modification &oth) const { return t < oth.t; }
} mdfy[N << 1];
struct query
{
	int t, l, r;
	bool operator < (const query &oth) const { return t < oth.t; }
} qry[N << 1];
void Modify(modification &opt)
{
	for(int p = opt.p; p < S; p += p & -p)
		o[p] += opt.v;
}
int Query(query &opt)
{
	int res = 0;
	for(int p = opt.r; p; p -= p & -p)
		res += o[p];
	for(int p = opt.l - 1; p; p -= p & -p)
		res -= o[p];
	return res;
}
signed main()
{
	scanf("%lld %lld", &n, &m);
	for(int i = 1; i <= n; i++)
	{
		scanf("%lld %lld %lld", &y[i], &lx[i], &rx[i]);
		if(lx[i] == 0 && rx[i] == 1000000) ans++;
		y[i]++; lx[i]++; rx[i]++;
		AddModification(lx[i] - 1, y[i], 1);
		AddModification(rx[i], y[i], -1);
	}
	for(int i = 1; i <= m; i++)
	{
		scanf("%lld %lld %lld", &x[i], &ly[i], &ry[i]);
		if(ly[i] == 0 && ry[i] == 1000000) ans++;
		x[i]++; ly[i]++; ry[i]++;
		AddQuery(x[i], ly[i], ry[i]);
	}
	sort(mdfy + 1, mdfy + m0 + 1);
	sort(qry + 1, qry + q0 + 1);
	int nowm = 1, nowq = 1;
	for(; nowm <= m0 && mdfy[nowm].t == 0; nowm++) Modify(mdfy[nowm]);
	for(int t = 1; t < S; t++)
	{
		for(; nowq <= q0 && qry[nowq].t == t; nowq++) ans += Query(qry[nowq]);
		for(; nowm <= m0 && mdfy[nowm].t == t; nowm++) Modify(mdfy[nowm]);
	}
	printf("%lld\n", ans);
	return 0;
}
posted @ 2020-08-23 18:08  syksykCCC  阅读(290)  评论(0编辑  收藏  举报