嘘~ 正在从服务器偷取页面 . . .

洛谷P4169 [Violet] 天使玩偶/SJY摆棋子 题解


洛谷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;
}

参考文献

[1] https://www.luogu.com.cn/article/dhqz4oov


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录