[BZOJ 2648] [Luogu P4169 ] [Violet]天使玩偶/SJY摆棋子

BZOJ传送门
洛谷传送门

题目描述

Ayu 在七年前曾经收到过一个天使玩偶,当时她把它当作时间囊埋在了地下。而七年后 的今天,Ayu 却忘了她把天使玩偶埋在了哪里,所以她决定仅凭一点模糊的记忆来寻找它。

我们把 Ayu 生活的小镇看作一个二维平面坐标系,而 Ayu 会不定时地记起可能在某个点 (x,y) ( x , y ) 埋下了天使玩偶;或者 Ayu 会询问你,假如她在 (x,y) ( x , y ) ,那么她离近的天使玩偶可能埋下的地方有多远。

因为 Ayu 只会沿着平行坐标轴的方向来行动,所以在这个问题里我们定义两个点之间的距离为dist(A,B)=|Ax-Bx|+|Ay-By|。其中 Ax 表示点 A的横坐标,其余类似。

输入输出格式

输入格式:

第一行包含两个整数n和m ,在刚开始时,Ayu 已经知道有n个点可能埋着天使玩偶, 接下来 Ayu 要进行m 次操作

接下来n行,每行两个非负整数 (xi,yi) ( x i , y i ) ,表示初始 n n 个点的坐标。

再接下来m 行,每行三个非负整数 t,xi,yi

如果 t=1 t = 1 ,则表示 Ayu 又回忆起了一个可能埋着玩偶的点 (xi,yi) 。

如果 t=2 t = 2 ,则表示 Ayu 询问如果她在点 (xi,yi) ( x i , y i ) ,那么在已经回忆出来的点里,离她近的那个点有多远

输出格式:

对于每个 t=2 t = 2 的询问,在单独的一行内输出该询问的结果。

输入输出样例

输入样例#1:
2 3 
1 1 
2 3 
2 1 2 
1 3 3 
2 4 2
输出样例#1:
1 
2

说明

n,m<=300000 n , m <= 300 000 , xi,yi<=1000000 x i , y i <= 1 000 000

解题分析

此题可用 CDQ C D Q KDTree K D − T r e e 解决(但 CDQ C D Q 常数过大会被卡T, 然而博主只会 CDQ C D Q , 或许是写的太丑常数大吧QAQ)。

首先, 我们要求的是 min(|xixj|+|yiyj|) m i n ( | x i − x j | + | y i − y j | ) , 如果保证 i i j的左下方即可消去绝对值符号, 即对于一个查询我们只需要知道 max(xj+yj) m a x ( x j + y j ) 即可。 显然这就变成了三维偏序问题, 而我们只需要分四类讨论即可。

PS:博主树状数组查询和修改方向写成相同的了, Debug了半天qwq。

另外, 这道题还有些小技巧:

如果我们按插入、查询时间为第一维, X X 坐标为第二维的话, 我们一开始不需要排序(因为是按时间顺序给出的操作), 但在分治过程中需要对X排序, 所以这回大大加大常数:

还是给出代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cctype>
#include <cstdlib>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 1000005
#define lowbit(i) (i & -i)
template <class T>
IN void in (T &x)
{
    x = 0; R char c = gc;
    W (!isdigit(c)) c = gc;
    W (isdigit(c))
    x = (x << 1) + (x << 3) + c - 48, c = gc;
}
struct Que
{
    int x, y, tim, id, typ;
}eve[MX], cpy[MX];
IN bool cmp2 (const Que &x, const Que &y) {return x.x == y.x ? x.typ < y.typ : x.x < y.x;}
int tree[MX], ans[MX];
int limit, dot, cnt, q, tot;
IN void add(R int pos, const int &del)
{W (pos <= limit) {if(tree[pos] < del) tree[pos] = del; pos += lowbit(pos);}}
IN int query(R int pos)
{
    int ret = 0;
    W (pos) {if(tree[pos] > ret) ret = tree[pos]; pos -= lowbit(pos);}
    return ret;
}
IN void clear(R int pos)
{
     W(pos <= limit) {if(tree[pos])tree[pos] = 0, pos += lowbit(pos); else return;}
}
void cdq(const int &lef, const int &rig)
{//无法直接通过x坐标得知排名, 并且X坐标在四次讨论中不断变化(当然也可以预处理, 但是博主觉得难写QAQ)所以需要每次sort
    int anss;
    if(lef == rig) return;
    int mid = lef + rig >> 1;
    cdq(lef, mid), 
    cdq(mid + 1, rig);
    std::sort(cpy + lef, cpy + 1 + mid, cmp2);
    std::sort(cpy + 1 + mid, cpy + 1 + rig, cmp2);
    int lb = lef, rb = mid + 1;
    W (233)
    {
        if(cpy[rb].typ)
        {
            W (cpy[lb].x <= cpy[rb].x && lb <= mid)
            {
                if(!cpy[lb].typ) add(cpy[lb].y, cpy[lb].x + cpy[lb].y);
                ++lb;
            }
            anss = query(cpy[rb].y);
            if(anss) ans[cpy[rb].id] = std::min(ans[cpy[rb].id], cpy[rb].x + cpy[rb].y - anss);
        }
        ++rb;
        if(rb > rig) break;
    }
    for (R int i = lef; i < lb; ++i) if(!cpy[i].typ) clear(cpy[i].y);
}
int main(void)
{
    memset(ans, 63, sizeof(ans));
    int a;
    in(dot), in(q);
    for (R int i = 1; i <= dot; ++i)
    {
        in(eve[i].x), in(eve[i].y), ++eve[i].x, ++eve[i].y;
        limit = std::max(eve[i].x, std::max(eve[i].y, limit));
    }
    cnt = dot;
    for (R int i = 1; i <= q; ++i)
    {
        ++cnt; eve[cnt].tim = i;
        in(a), in(eve[cnt].x), in(eve[cnt].y), ++eve[cnt].x, ++eve[cnt].y;
        if(a ^ 1) eve[cnt].typ = 1, eve[cnt].id = ++tot;
        limit = std::max(eve[cnt].x, std::max(limit, eve[cnt].y));
    }
    for (R int i = 1; i <= cnt; ++i) cpy[i] = eve[i];
    limit++;
    cdq(1, cnt);
    for (R int i = 1; i <= cnt; ++i) eve[i].x = -eve[i].x + limit, cpy[i] = eve[i];
    cdq(1, cnt);
    for (R int i = 1; i <= cnt; ++i) eve[i].y = -eve[i].y + limit, cpy[i] = eve[i];
    cdq(1, cnt);
    for (R int i = 1; i <= cnt; ++i) eve[i].x = -eve[i].x + limit, cpy[i] = eve[i];
    cdq(1, cnt);
    for (R int i = 1; i <= tot; ++i) printf("%d\n", ans[i]);
}

以时间为第一关键字排序是不好优化了, 所以我们考虑以 X X 坐标为第一关键字排序, 而操作时间在输入时就可以得到, 所以每次只需要O(N)时间就可以完成排序操作。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cctype>
#include <cstdlib>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 1000005
#define lowbit(i) (i & -i)
inline char nc()
{
    static const int buflen=1e6;
    static char buf[buflen],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,buflen,stdin),p1==p2)?EOF:*p1++;
}
template <class T>
IN void in (T &x)
{
    x = 0; R char c = nc();
    W (!isdigit(c)) c = nc();
    W (isdigit(c))
    x = (x << 1) + (x << 3) + c - 48, c = nc();
}
struct Que
{
    int x, y, tim, id, typ;
}cpy[MX], buf[MX];
IN bool cmp (const Que &x, const Que &y) {return x.x == y.x ? x.typ < y.typ : x.x < y.x;}
int tree[MX], ans[MX];
int limit, dot, cnt, q, tot;
IN void add(R int pos, const int &del)
{W (pos <= limit) {if(tree[pos] < del) tree[pos] = del; pos += lowbit(pos);}}
IN int query(R int pos)
{
    int ret = 0;
    W (pos) {if(tree[pos] > ret) ret = tree[pos]; pos -= lowbit(pos);}
    return ret;
}
IN void clear(R int pos)
{
     W(pos <= limit) tree[pos] = 0, pos += lowbit(pos);
}
void cdq(const int &lef, const int &rig)
{
    int anss;
    if(lef == rig) return;
    int mid = lef + rig >> 1;
    int lb = lef, rb = mid + 1;
    for (R int i = lef; i <= rig; ++i)
    {
        if(cpy[i].tim > mid) buf[rb++] = cpy[i];
        else buf[lb++] = cpy[i];
    }
    for (R int i = lef; i <= rig; ++i) cpy[i] = buf[i];//要将排序好的结果传到下一层, 原因请大家想一想
    rb = mid + 1, lb = lef;
    W (233)
    {
        if(cpy[rb].typ)
        {
            W (cpy[lb].x <= cpy[rb].x && lb <= mid)
            {
                if(!cpy[lb].typ) add(cpy[lb].y, cpy[lb].x + cpy[lb].y);
                ++lb;
            }
            anss = query(cpy[rb].y);
            if(anss) ans[cpy[rb].id] = std::min(ans[cpy[rb].id], cpy[rb].x + cpy[rb].y - anss);
        }
        ++rb;
        if(rb > rig) break;
    }
    for (R int i = lef; i < lb; ++i) if(!cpy[i].typ) clear(cpy[i].y);//清空用过的树状数组, memset血T无疑
    cdq(lef, mid),  
    cdq(mid + 1, rig);
}
int main(void)
{
    memset(ans, 63, sizeof(ans));
    int a;
    in(dot), in(q);
    for (R int i = 1; i <= dot; ++i)
    {
        ++cnt; cpy[cnt].tim = i;
        in(cpy[cnt].x), in(cpy[cnt].y), ++cpy[cnt].x, ++cpy[cnt].y;
        limit = std::max(cpy[cnt].x, std::max(cpy[cnt].y, limit));
    }
    for (R int i = 1; i <= q; ++i)
    {
        ++cnt; cpy[cnt].tim = cnt;
        in(a), in(cpy[cnt].x), in(cpy[cnt].y), ++cpy[cnt].x, ++cpy[cnt].y;
        if(a ^ 1) cpy[cnt].typ = 1, cpy[cnt].id = ++tot;
        limit = std::max(cpy[cnt].x, std::max(limit, cpy[cnt].y));
    }
    limit++;
    std::sort(cpy + 1, cpy + 1 + cnt, cmp);
    cdq(1, cnt);
    for (R int i = 1; i <= cnt; ++i) cpy[i].x = -cpy[i].x + limit;
    std::sort(cpy + 1, cpy + 1 + cnt, cmp);
    cdq(1, cnt);
    for (R int i = 1; i <= cnt; ++i) cpy[i].y = -cpy[i].y + limit;
    std::sort(cpy + 1, cpy + 1 + cnt, cmp);
    cdq(1, cnt);
    for (R int i = 1; i <= cnt; ++i) cpy[i].x = -cpy[i].x + limit;
    std::sort(cpy + 1, cpy + 1 + cnt, cmp);
    cdq(1, cnt);
    for (R int i = 1; i <= tot; ++i) printf("%d\n", ans[i]);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值