题目链接:传送门

关于CDQ分治(参考李煜东《算法竞赛进阶指南》):

对于一系列操作,其中的任何一个询问操作,其结果必然等价于:初始值 + 此前所有的修改操作产生的影响。

假设共有 $m$ 次操作,对于任意的满足 $1 \le l \le r \le m$ 的正整数 $l,r$,定义 $solve(l,r)$ 为:对于任意的正整数 $k \in [l,r]$,若第 $k$ 次操作为询问操作,则计算第 $l \sim k-1$ 次操作中的修改操作对第 $k$ 次查询的影响。$solve(l,r)$ 可以通过分治来计算:

  1. 设 $mid = (l+r)>>1$,递归计算 $solve(l,mid)$ 和 $solve(mid+1,r)$。
  2. 计算第 $l \sim mid$ 次操作中所有修改操作对第 $mid+1 \sim r$ 次操作中所有询问操作的影响。

同时,根据 $solve(l,r)$ 的定义,显然 $solve(1,m)$ 即为原始问题(不考虑初始值的情况下),而当 $l=r$ 时 $solve(l,r)$ 显然不需要进行任何计算。

第 2 部分的操作,其实是一个静态问题,因为修改和询问是完全被前后分开的。类比于经典分治算法归并排序,我们只要让计算第 2 部分的时间复杂度只和 $r-l$ 线性相关,就可以使得总的时间复杂度为 $O(m \log m)$。

这种离线分治算法是基于时间顺序对操作序列进行分治的,因此称为基于时间的分治算法,或者更加广泛的名称CDQ分治

题解:

这个题,剥离题目背景,无非就是在二维平面上,在若干时刻放置一个点在某个位置,同时在若干时刻查询某个位置与其最近邻的距离。

自然而然就会想起KDTree,不过我还不会带修改的KDTree怎么办,那不妨考虑一下一个简单的问题,假设没有修改操作,只在一开始就给定你平面上若干个点,后续全部都是查询操作。

即:给定平面上 $n$ 个点 $(x_i,y_i)$,有 $m$ 次查询操作,每次询问距离 $(x,y)$ 位置的最近的点有多远(KDTree板子题)。显然,答案就是 $\min_{1 \le i \le n}\{ |x - x_i| + |y - y_i| \}$。

方便起见,为了去掉绝对值符号,不妨把询问变成询问四个方向(第 $1,2,3,4$ 象限方向)上距离最近的点。那么,以第三象限方向上的询问为例,其答案就变成:

$\min_{1 \le i \le n}\{ x - x_i + y - y_i \} = (x+y) - \max_{1 \le i \le n}\{x_i + y_i\}$

其中,$\forall x_i,y_i$ 满足 $x_i \le x$ 且 $y_i \le y$。

此时,可以考虑树状数组优化时间复杂度,先把所有平面上的点和询问中的位置按横坐标从小到大排个序,然后依次进行扫描。

  1. 若扫描到一个点 $(x_i,y_i)$,则在树状数组中把第 $y_i$ 个位置上的值和 $x_i+y_i$ 取较大值。
  2. 若扫描到一个询问的位置 $(x,y)$,则在树状数组中查询区间 $[0,y]$ 上的最大值。

显然,按照横坐标从小到大扫描,使得每次询问时,树状数组中存储的值都是 $x$ 值不大于当前位置的点,而在区间 $[0,y]$ 的查询就迫使所有被考虑的点的 $y$ 值都是不超过当前位置的。

时间复杂度 $O((n+m)\log(n+m))$。

然后,我们回到原来的问题,对于原问题,我们使用CDQ分治,那么需要考虑的问题就变成了 $solve(l,r)$ 中,如何计算第 $l \sim mid$ 次操作中所有修改操作对第 $mid+1 \sim r$ 次操作中所有询问操作的影响。换句话说,如何计算第 $l \sim mid$ 次操作中所有添加进来的点,对第 $mid+1 \sim r$ 次操作中所有询问的影响。这样一看,这不就是上面讲到的简化版问题吗。

当然,为了保证每次计算静态问题的时间复杂度仅和 $r-l$ 相关,不能每次都建立一个新的树状数组,必须在每次计算 $solve(l,r)$ 后,把对树状数组的修改都撤销掉。也就是说,要保证对于每个 $solve(l,r)$,在进入和离开函数时,树状数组都是空的。

这样一来,不妨把初始的 $n$ 个点也看做修改操作,对于总的 $n+m$ 次操作,总时间复杂度就是 $O((n+m) \log^2 (n+m))$。

AC代码:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int MAX=1e6+;
  4. const int INF=0x3f3f3f3f;
  5.  
  6. int n,m;
  7. struct Rec{
  8. int t,x,y;
  9. Rec(){}
  10. Rec(int _t,int _x,int _y) {
  11. t=_t, x=_x, y=_y;
  12. }
  13. bool operator<(const Rec &b)const
  14. {
  15. if(x==b.x) return y<b.y;
  16. else return x<b.x;
  17. }
  18. }o[MAX];
  19. int tot;
  20. Rec s[MAX];
  21. int ans[MAX];
  22.  
  23. int C[MAX],N;
  24. inline int lowbit(int x){return x&-x;}
  25. int ask(int pos) {
  26. int res=-INF;
  27. for(;pos>=;pos-=lowbit(pos)) res=max(res,C[pos]);
  28. return res;
  29. }
  30. void upd(int pos,int val) {
  31. for(;pos<=N;pos+=lowbit(pos)) C[pos]=max(C[pos],val);
  32. }
  33.  
  34. //第一象限:- x - y - max(- xi - yi)
  35. //第二象限:+ x - y - max(+ xi - yi)
  36. //第三象限:+ x + y - max(+ xi + yi)
  37. //第四象限:- x + y - max(- xi + yi)
  38. void calc(int st,int ed,int d,int sx,int sy)
  39. {
  40. for(int i=st;i!=ed;i+=d)
  41. {
  42. int pos=(sy==)?s[i].y:N-s[i].y;
  43. int val=sx*s[i].x+sy*s[i].y;
  44. if(o[s[i].t].t==) upd(pos,val);
  45. else ans[s[i].t]=min(ans[s[i].t],val-ask(pos));
  46. }
  47. for(int i=st;i!=ed;i+=d)
  48. {
  49. int pos=(sy==)?s[i].y:N-s[i].y;
  50. if(o[s[i].t].t==) for(;pos<=N;pos+=lowbit(pos)) C[pos]=-INF;
  51. }
  52. }
  53.  
  54. void cdqdiv(int l,int r)
  55. {
  56. if(l>=r) return;
  57. int mid=(l+r)>>;
  58. cdqdiv(l,mid);
  59. cdqdiv(mid+,r);
  60. tot=;
  61. for(int i=l;i<=mid;i++) if(o[i].t==) s[++tot]=Rec(i,o[i].x,o[i].y);
  62. for(int i=mid+;i<=r;i++) if(o[i].t==) s[++tot]=Rec(i,o[i].x,o[i].y);
  63. sort(s+,s+tot+);
  64. calc(tot,,-,-,-); //第一象限
  65. calc(,tot+,,,-); //第二象限
  66. calc(,tot+,,,); //第三象限
  67. calc(tot,,-,-,); //第四象限
  68. }
  69.  
  70. int main()
  71. {
  72. scanf("%d%d",&n,&m); m+=n;
  73. for(int i=;i<=n;i++) o[i].t=, scanf("%d%d",&o[i].x,&o[i].y), ++o[i].y;
  74. for(int i=n+;i<=m;i++) scanf("%d%d%d", &o[i].t,&o[i].x,&o[i].y), ++o[i].y;
  75.  
  76. for(int i=;i<=m;i++) N=max(N,o[i].y); N++;
  77. for(int i=;i<=N;i++) C[i]=-INF;
  78.  
  79. memset(ans,0x3f,sizeof(ans));
  80. cdqdiv(,m);
  81. for(int i=;i<=m;i++) if(o[i].t==) printf("%d\n",ans[i]);
  82. }

CH 4701 - 天使玩偶 - [CDQ分治]的更多相关文章

  1. [BZOJ2716] [Violet 3]天使玩偶(CDQ分治)

    [BZOJ2716] [Violet 3]天使玩偶(CDQ分治) 题面 Ayu 在七年前曾经收到过一个天使玩偶,当时她把它当作时间囊埋在了地下.而七年后 的今天,Ayu 却忘了她把天使玩偶埋在了哪里, ...

  2. BZOJ 2716: [Violet 3]天使玩偶( CDQ分治 + 树状数组 )

    先cdq分治, 然后要处理点对答案的贡献, 可以以询问点为中心分成4个区域, 然后去掉绝对值(4种情况讨论), 用BIT维护就行了. --------------------------------- ...

  3. 洛谷P4169 天使玩偶 CDQ分治

    还是照着CDQ的思路来. 但是有一些改动: 要求4个方向的,但是可爱的CDQ分治只能求在自己一个角落方向上的.怎么办?旋转!做4次就好了. 统计的不是和,而是——max!理由如下: 设当前点是(x,y ...

  4. 天使玩偶:CDQ分治

    这道好(du)题(liu)还是很不错的 挺锻炼代码能力和不断优化 卡常的能力的. 对于 每次询问 我都可以将其分出方向 然后 写 也就是针对于4个方向 左下 左上 右下 右上 这样的话 就成功转换了问 ...

  5. [bzoj] 2716 天使玩偶 || CDQ分治

    原题 已知n个点有天使玩偶,有m次操作: 操作1:想起来某个位置有一个天使玩偶 操作2:询问离当前点最近的天使玩偶的曼哈顿距离 显然的CDQ问题,三维分别为时间,x轴,y轴. 但是这道题的问题在于最近 ...

  6. BZOJ 2716 Violet 3 天使玩偶 CDQ分治

    题目大意:初始给定平面上的一个点集.提供两种操作: 1.将一个点增加点集 2.查询距离一个点最小的曼哈顿距离 K-D树是啥...不会写... 我仅仅会CDQ分治 对于一个询问,查询的点与这个点的位置关 ...

  7. BZOJ.2716.[Violet3]天使玩偶(CDQ分治 坐标变换)

    题目链接 考虑对于两个点a,b,距离为|x[a]-x[b]|+|y[a]-y[b]|,如果a在b的右上,那我们可以把绝对值去掉,即x[a]+y[a]-(x[b]+y[b]). 即我们要求满足x[b]& ...

  8. BZOJ 2716 [Violet 3]天使玩偶 (CDQ分治、树状数组)

    题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=2716 怎么KD树跑得都那么快啊..我写的CDQ分治被暴虐 做四遍CDQ分治,每次求一个 ...

  9. CH4701 天使玩偶

    题意 4701 天使玩偶 0x40「数据结构进阶」例题 描述 题目PDF 样例输入 2 3 1 1 2 3 2 1 2 1 3 3 2 4 2 样例输出 1 2 来源 石家庄二中Violet 3杯省选 ...

随机推荐

  1. String为什么是不可变的?

    面试官Q1:请问为什么String是不可变的,能谈谈吗? 我们知道不管是面试初级.中级还是高级Java开发工程师,String永远都是一个绕不开的话题,而且问的问题也是各不相同,下面我们从几个角度来看 ...

  2. ASP.NET MVC多语言 仿微软网站效果(转)

    本文转自: https://blog.csdn.net/Cooldiok/article/details/7831351 2017年10月22日 21:31:22 Cooldiok 微软作为ASP.N ...

  3. LeetCode OJ 238. Product of Array Except Self 解题报告

        题目链接:https://leetcode.com/problems/product-of-array-except-self/ 238. Product of Array Except Se ...

  4. rand()产生随机数 及其和clock()的不同

    rand()使用 首先我们要对rand&srand有个总体的看法:srand初始化随机种子,rand产生随机数. 定义函数 : int rand(void) 函数说明 :因为rand的内部实现 ...

  5. python(60):configparser 函数,配置文件

    ConfigParser模块学习 介绍 ConfigParser模块在python中是用来读取配置文件,配置文件的格式跟windows下的ini配置文件相似,可以包含一个或多个节(section),每 ...

  6. linux每日命令(38):iostat命令

    Linux系统中的 iostat是I/O statistics(输入/输出统计)的缩写,iostat工具将对系统的磁盘操作活动进行监视.它的特点是汇报磁盘活动统计情况,同时也会汇报出CPU使用情况.同 ...

  7. SDL示例一:实现七段数码管的显示

    [时间:2017-05] [状态:Open] [关键词:sdl2,数字,七段数码管,图形显示,示例代码] 0 引言 本文是针对我的step-into-sdl2/7LedDigit的原理介绍,有兴趣的可 ...

  8. oracle 11g rac asm磁盘组增加硬盘

    要增加磁盘的磁盘组为:DATA 要增加的磁盘为: /dev/sde1 在第一个节点上:[root@rac1 ~]# fdisk /dev/sdeDevice contains neither a va ...

  9. Java多线程系列——线程池原理之 ThreadPoolExecutor

    ThreadPoolExecutor 简介 ThreadPoolExecutor 是线程池类. 通俗的讲,它是一个存放一定数量线程的线程集合.线程池允许多个线程同时运行,同时运行的线程数量就是这个线程 ...

  10. OAuth 2.0 C# 版

    using System; using System.Collections.Generic; using System.Dynamic; using System.Linq; using Syste ...