洛谷P4169 [Violet] 天使玩偶/SJY摆棋子 题解
题目链接:P4169 [Violet] 天使玩偶/SJY摆棋子
题意:
Ayu 在七年前曾经收到过一个天使玩偶,当时她把它当作时间囊埋在了地下。而七年后 的今天,Ayu 却忘了她把天使玩偶埋在了哪里,所以她决定仅凭一点模糊的记忆来寻找它。
我们把 Ayu 生活的小镇看作一个二维平面坐标系,而 Ayu 会不定时地记起可能在某个点 $(x, y)$ 埋下了天使玩偶;或者 Ayu 会询问你,假如她在 $(x,y)$,那么她离近的天使玩偶可能埋下的地方有多远。
因为 Ayu 只会沿着平行坐标轴的方向来行动,所以在这个问题里我们定义两个点之间的距离为 $\operatorname{dist}(A,B)=|A_x-B_x|+|A_y-B_y|$。其中 $A_x$ 表示点 $A$ 的横坐标,其余类似。
输入格式:
第一行包含两个整数 $n$ 和 $m$,在刚开始时,Ayu 已经知道有 $n$ 个点可能埋着天使玩偶, 接下来 Ayu 要进行 $m$ 次操作。
接下来 $n$ 行,每行两个非负整数 $(x_i,y_i)$,表示初始 $n$ 个点的坐标。
再接下来 $m$ 行,每行三个非负整数 $t,x_i,y_i$。
- 如果 $t=1$,则表示 Ayu 又回忆起了一个可能埋着玩偶的点 $(x_i,y_i)$。
- 如果 $t=2$,则表示 Ayu 询问如果她在点 $(x_i,y_i)$,那么在已经回忆出来的点里,离她近的那个点有多远
输出格式:
对于每个 $t=2$ 的询问,在单独的一行内输出该询问的结果。
数据范围:
保证 $1 \leq n,m\leq 3 \times 10^5$,$0 \leq x_i,y_i \leq 10^6$。
这道 cdq 分治还是挺有趣的,因为传统的 cdq 通常计算 $x_i \le x$ 且 $y_i \le y$ 的贡献
而在本题中,因为距离是曼哈顿距离,所以在其他象限(以某个固定的中间位置为原点)的点也可能产生贡献。
那么怎么才能计算他们的贡献呢?诶,既然点不能动,那我们直接翻转平面不就好了吗?
在曼哈顿距离中,仅仅翻转一下平面(或者说对点统一进行对坐标轴的轴对称),不会对答案产生影响
于是我们可以跑四遍 cdq 分治,把四个象限的点都分别看作在 $x_i \le x$ 且 $y_i \le y$ 的象限,计算贡献
时间复杂度 $\mathcal{O}(n \log^2 n)$
代码:
#include <bits/stdc++.h>
using namespace std;
// #define int long long
#define INF 0x3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(1e6 + 15))
int n, m, tot, lx, ly, rx, ry, tr[N], ans[N];
struct node { int x, y, id, type; } a[N], p[N], q[N];
int lowbit(int x) { return x & (-x); }
void upd(int x, int v) { for(int i = x; i <= ly; i += lowbit(i)) up(tr[i], v); }
int qry(int x, int r = 0) { for(int i = x; i; i -= lowbit(i)) up(r, tr[i]); return r; }
void clear(int x) { for(int i = x; i <= ly; i += lowbit(i)) if(tr[i]) tr[i] = 0; else break; }
void clear()
{
rx = ry = tot = 0;
for(int i = 1; i <= n; i++)
if(p[i].type == 2) { up(rx, p[i].x), up(ry, p[i].y); }
for(int i = 1; i <= n; i++)
if(p[i].x <= rx && p[i].y <= ry) q[++tot] = p[i];
for(int i = 1; i <= tot; i++) p[i] = q[i];
}
void cdq(int l, int r)
{
if(l == r) return; int mid = (l + r) >> 1;
cdq(l, mid); cdq(mid + 1, r); int j = l, cnt = 0;
for(int i = mid + 1; i <= r; i++)
{
while(p[j].x <= p[i].x && j <= mid)
{
if(p[j].type == 1) upd(p[j].y, p[j].x + p[j].y);
q[++cnt] = p[j++];
}
if(p[i].type == 2) {
int tmp = qry(p[i].y);
if(tmp) down(ans[p[i].id], p[i].x + p[i].y - tmp);
}
q[++cnt] = p[i];
}
for(int i = l; i < j; i++) clear(p[i].y);
while(j <= mid) q[++cnt] = p[j++];
for(int i = 1; i <= cnt; i++) p[l + i - 1] = q[i];
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
bool ok = 0; cin >> n >> m;
for(int i = 1, x, y; i <= n; i++)
{
cin >> x >> y; ++x; ++y;
p[i] = {x, y, i, 1}; up(lx, x); up(ly, y);
}
for(int i = 1, x,y,op; i <= m; i++)
{
cin >> op >> x >> y; ++x; ++y; ++n;
p[n] = {x, y, n, op}; up(lx, x); up(ly, y); ok |= (op == 2);
}
if(!ok) return 0;
for(int i = 1; i <= n; i++) a[i] = p[i];
memset(ans, 0x3f, sizeof(ans)); ++lx, ++ly;
clear(); cdq(1, tot);
for(int i = 1; i <= n; i++) { p[i] = a[i]; p[i].x = lx - p[i].x; }
clear(); cdq(1, tot);
for(int i = 1; i <= n; i++) { p[i] = a[i]; p[i].y = ly - p[i].y; }
clear(); cdq(1, tot);
for(int i = 1; i <= n; i++) { p[i] = a[i]; p[i].x = lx - p[i].x; p[i].y = ly - p[i].y; }
clear(); cdq(1, tot);
for(int i = 1; i <= n; i++) if(a[i].type == 2) cout << ans[i] << '\n';
return 0;
}
参考文献: