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

传送门

二维平面修改+查询,cdq分治可以解决。

求关于某个点曼哈顿距离(x,y坐标)最近的点——dis(A,B) = |Ax-Bx|+|Ay-By|

但是如何去掉绝对值呢?

查看题解发现假设所有的点都在查询点的左下方,dis(A,B) = (Ax-Bx)+(Ay-By) = (Ax+Ay)-(Bx+By)

只要求满足Bx<Ax,By<Ay且Bx,By之和最大的点就好了。

那么如何把所有的点转化到该查询的左下呢?

对于每个查询,可以把一、二、四象限的点都通过对称转移到第三象限。但查询很多,不可能一个个翻转。

换个思路,如果把整个平面翻转三次,进行四次cdq分治,每次都只考虑左下的点,所有的点就都遍历到了!

记录最大的x或y值为边界len,每次沿len翻转。例如沿y轴翻转时,x = len-x

那么每个操作有三维——时间、x坐标、y坐标

时间在输入时已经排好了;x归并排序;y仿照陌上花开,用树状数组记录。

优化 & 注意

这道题坑点超级多...而且四次cdq分治会得到一个感人的复杂度,所以必须考虑优化,卡一卡常数(我选择吸氧)

  • cdq内的归并排序代替每次sort。
  • 因为每次cdq完顺序会被打乱,如果重新按时间O(nlogn)排序,不如每次存入一个临时数组,然后O(n)直接复制过去。

  但是ans需要存入初始的数组中,所以结构体需要一个.id来记录打乱前的时间,也就是原数组下标。赋值应该写a[b[t2].id].ans,而不是a[t2].ans。

  并且,由于每次查询点的x,y也会更改,所以ans里不能直接存max(Bx+By),而应该为min((Ax+Ay)-(Bx+By))。

  • 如果某个点在坐标轴上,那么它的x或y为0。存入树状数组时,会因为lowbit()==0而陷入死循环。所以存入时,将x,y分别+1。

  同样的,如果某个点在翻转边界len上,翻转时也会变为0。所以len也要++。

  • 考虑这样一种情况:某一点非常靠近边界,导致某次翻转时,没有点在它的左下。这样查询时默认返回了0。

  当前的“原点”比实际上的点离该查询点更近,这样最终的距离就成了这个点到原点的距离,但原点是不存在的(经过刚刚的更改,已经没有x或y坐标为0的点)

  为避免这种情况,当查询时需要特判,若为0则返回-INF。

  • 由于初始值——前n个点一定是修改操作,可以把它们直接排好序,不用递归检验是否有查询。(不过我觉得有点麻烦就没写)

这道题的代码不难,但是细节特别多,很难debug...写的时候思路一定要清晰了!

代码如下

// luogu-judger-enable-o2
#include<cstdio>
#include<iostream>
#define MogeKo qwq
using namespace std;
const int maxn = 1e7+10;
const int INF = 2e7+10;
int n,q,opt,x,y,len;

struct node {
    int x,y,type,id,ans;
} a[maxn],b[maxn],tem[maxn];

struct BIT {
    int m[maxn];
    int lowbit(int x) {
        return x & -x;
    }
    void update(int x,int v) {
        for(; x <= len; x+= lowbit(x))
            m[x] = max(m[x],v);
    }
    int query(int x) {
        int ans = 0;
        for(; x; x-=lowbit(x))
            ans = max(ans,m[x]);
        return ans?ans:-INF;
    }
    void clear(int x) {
        for(; m[x]; x+= lowbit(x))
            m[x] = 0;
    }
} tree;

void cdq(int L,int R) {
    if(L == R) return;
    int mid = L+R >> 1;
    cdq(L,mid),cdq(mid+1,R);
    int t1 = L,t2 = mid+1;
    int k = L;
    while(t2 <= R) {
        while(t1 <= mid && b[t1].x <= b[t2].x) {
            if(b[t1].type == 1)
                tree.update(b[t1].y, b[t1].x+b[t1].y);
            tem[k++] = b[t1++];
        }
        if(b[t2].type == 2)
            a[b[t2].id].ans = min(a[b[t2].id].ans,b[t2].x+b[t2].y-tree.query(b[t2].y));
        tem[k++] = b[t2++];
    }
    for(int i = L; i <= t1-1; i++)
        if(b[i].type == 1) tree.clear(b[i].y);
    while(t1 <= mid) tem[k++] = b[t1++];
    for(int i = L;i <= R;i++) b[i] = tem[i];
}

void solve(int rx,int ry) {
    for(int i = 1; i <= n+q; i++) {
        b[i] = a[i];
        if(rx) b[i].x = len - b[i].x;
        if(ry) b[i].y = len - b[i].y;
    }
    cdq(1,n+q);
}

int main() {
    scanf("%d%d",&n,&q);
    for(int i = 1; i <= n; i++) {
        scanf("%d%d",&x,&y);
        a[i].type = 1;
        a[i].id = i;
        a[i].x = ++x;
        a[i].y = ++y;
        len = max(len,max(x,y));
    }
    for(int i = n+1; i <= n+q; i++) {
        scanf("%d%d%d",&opt,&x,&y);
        a[i].type = opt;
        a[i].id = i;
        a[i].x = ++x;
        a[i].y = ++y;
        a[i].ans = INF;
        len = max(len,max(x,y));
    }
    len++;
    solve(0,0),solve(0,1),solve(1,0),solve(1,1);
    for(int i = n+1; i <= n+q; i++)
        if(a[i].type == 2) printf("%d\n",a[i].ans);
    return 0;
}

原文地址:https://www.cnblogs.com/mogeko/p/10503518.html

时间: 2024-10-09 15:06:22

Luogu P4169 [Violet]天使玩偶/SJY摆棋子的相关文章

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

1 // luogu-judger-enable-o2 2 #include<cstdio> 3 #include<iostream> 4 using namespace std; 5 const int maxn=1e7+10; 6 const int inf=2e7+7; 7 struct node 8 { 9 int x,y,id,ans,time; 10 }a[maxn],b[maxn],tt[maxn];; 11 int flag; 12 int len,x,y; 13

[Violet]天使玩偶/SJY摆棋子

Luogu4169 每次操作加入平面上一个点 , 或者询问离一个点最近的点的距离 CDQ分治模板 \(1.\)solve(l,mid); \(2.\)solve(mid+1,r); \(3.\)计算\([l,mid]\)中修改操作对\([mid+1,r]\)中查询操作的影响 /* ---------------------- [CDQ分治] 天使玩偶 1.solve(l,mid); 2.solve(mid+1,r); 3.计算[l,mid]中修改操作对[mid+1,r]中查询操作的影响 ----

[Luogu4169][Violet]天使玩偶/SJY摆棋子

luogu 题意 一个平面上有\(n\)个点,\(m\)次操作,每次新增一个点,或者是询问离某个点最近的点的距离.这里的距离是曼哈顿距离. \(n,m\le3*10^5\) sol 写一发\(CDQ\). 只考虑询问点在其他点的右上方的情况,假设询问点是\(A\),那么所求的距离就是\((X_A-X_i)+(Y_A-Y_i)=(X_A+Y_A)-(X_i+Y_i)\). 所以我们只需要找出满足\(X_i \le X_A,Y_i \le Y_A\)中\(X_i+Y_i\)的最大值就好了. \(CD

【bzoj2648&amp;bzoj2716】[Violet3]天使玩偶&amp;SJY摆棋子【kd树】

1BK捌脊允4iyo松纲06http://weibo.com/u/6264971116 382u60L86vl戎导屯http://www.docin.com/nqra496 u23j6h0敢d缴遮ahttp://www.facebolw.com/space/2104191 86褐2咕6镜y米0uhttp://huiyi.docin.com/sina_6352810924 160hA坷Yk闭苫6尾ehttp://shequ.docin.com/sina_6267166088 侍4E0pf范星17南

SJY摆棋子&amp;&amp;[Violet 3]天使玩偶

SJY摆棋子 https://www.lydsy.com/JudgeOnline/problem.php?id=2648 [Violet 3]天使玩偶 https://www.lydsy.com/JudgeOnline/problem.php?id=2716 参考博客:https://blog.csdn.net/Littlewhite520/article/details/78284697 KDtree模板题,带插入 1 #include<iostream> 2 #include<cst

【BZOJ-2648&amp;2716】SJY摆棋子&amp;天使玩偶 KD Tree

2648: SJY摆棋子 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 2459  Solved: 834[Submit][Status][Discuss] Description 这天,SJY显得无聊.在家自己玩.在一个棋盘上,有N个黑色棋子.他每次要么放到棋盘上一个黑色棋子,要么放上一个白色棋子,如果是白色棋子,他会找出距离这个白色棋子最近的黑色棋子.此处的距离是 曼哈顿距离 即(|x1-x2|+|y1-y2|) .现在给出N<=500000

BZOJ 2648(SJY摆棋子-KD_Tree)

2648: SJY摆棋子 Time Limit: 20 Sec  Memory Limit: 128 MB Submit: 1180  Solved: 391 [Submit][Status][Discuss] Description 这天,SJY显得无聊.在家自己玩.在一个棋盘上,有N个黑色棋子.他每次要么放到棋盘上一个黑色棋子,要么放上一个白色棋子,如果是白色棋子,他会找出距离这个白色棋子最近的黑色棋子.此处的距离是 曼哈顿距离 即(|x1-x2|+|y1-y2|) .现在给出N<=5000

【BZOJ2648】SJY摆棋子 [KD-tree]

SJY摆棋子 Time Limit: 20 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description 这天,SJY显得无聊.在家自己玩.在一个棋盘上,有N个黑色棋子.他每次要么放到棋盘上一个黑色棋子,要么放上一个白色棋子,如果是白色棋子,他会找出距离这个白色棋子最近的黑色棋子.此处的距离是 曼哈顿距离 即(|x1-x2|+|y1-y2|) .现在给出N个初始棋子,和M个操作.对于每个白色棋子,输出距离这个白色棋子最近的黑色棋子的

BZOJ 2648 SJY摆棋子 / 2716 Violet 3 天使玩偶 K-D树

题目大意:平面上有一些点,问一个点周围离它最近的点的曼哈顿距离是多少.支持动态加点. 思路:CDQ分治可以离线解决,但是SJY是卡CDQ的,天使玩偶可以过.毕竟K-D树的O(sqrt(n))的时间复杂度摆在那. K-D树理解起来其实不难,有k个维度的时候,每一层按照一个维度排序,取出按照这个维度排序的中位数,当作这个块的根,然后将这个块分开.还有一个比较重要的东西就是估价函数,这个函数根据不同的题可能不同.股价函数的主要用途就是对搜索进行剪枝,如果估价函数就已经大于当前的最优答案了,那就不用搜这