前言:我们又回到了这道题,不过这次我们选择一个常数小一点的算法:CDQ

Description

Input

Output

Sample Input

100 100
81 23
27 16
52 58
44 24
25 95
34 2
96 25
8 14
97 50
97 18
64 3
47 22
55 28
89 37
75 45
67 22
90 8
65 45
68 93
87 8
61 45
69 72
38 57
58 76
45 34
88 54
27 8
35 34
70 81
25 24
97 97
4 43
39 38
82 68
27 58
2 21
92 88
96 70
97 29
14 53
6 42
1 2
35 84
64 88
63 57
53 40
82 59
49 56
75 72
29 30
50 1
40 83
52 94
22 35
39 1
94 88
89 96
79 46
33 75
31 42
33 95
6 83
90 66
37 54
35 64
17 66
48 37
30 8
95 51
3 51
90 33
29 48
94 78
53 7
1 26
73 35
18 33
99 78
83 59
23 87
4 17
53 91
98 3
54 82
85 92
77 8
56 74
4 5
63 1
26 8
42 15
48 98
27 11
70 98
36 9
78 92
34 40
42 82
64 83
75 47
2 51 55
1 7 62
2 21 62
1 36 39
1 35 89
1 84 15
2 19 24
1 58 53
2 52 34
1 98 49
1 4 100
1 17 25
1 30 56
1 69 43
2 57 23
2 23 13
1 98 25
2 50 27
1 84 63
2 84 81
2 84 77
1 60 23
2 15 27
1 9 51
1 31 11
1 96 56
2 20 85
1 46 32
1 60 88
2 92 48
1 68 5
2 90 17
1 16 46
2 67 5
2 29 83
1 84 70
2 68 27
1 99 33
2 39 89
2 38 28
1 42 3
1 10 60
2 56 29
2 12 60
2 46 51
2 15 73
1 93 42
1 78 82
1 66 20
1 46 17
2 48 5
1 59 61
1 87 59
2 98 72
1 49 3
2 21 10
1 15 4
1 48 14
2 67 75
2 83 77
1 88 65
2 100 93
2 58 83
1 29 80
2 31 88
2 92 94
1 96 66
1 61 82
2 87 24
1 64 83
1 28 87
2 72 90
2 7 3
1 86 3
2 26 53
2 71 2
2 88 24
1 69 60
1 92 44
2 74 94
1 12 78
2 1 2
1 4 73
1 58 5
1 62 14
2 64 58
2 39 45
1 99 27
1 42 21
1 87 2
2 16 98
2 17 21
2 41 20
1 46 72
1 11 62
2 68 29
1 64 66
2 90 42
2 63 35
1 64 71

Sample Output

3
8
6
7
7
6
6
12
11
4
5
6
8
1
7
6
4
9
2
2
8
9
6
4
7
5
8
7
5
5
5
7
7
5
6
6
8
6
0
2
7
12
4
2
8
3
10

HINT

Source

Vani原创 欢迎移步 OJ2648

[ Submit][ Status][ Discuss]

分析:
CDQ,第一件事就是寻找三维偏序:

  • 时间
  • x坐标
  • y坐标

但是这道题有一点麻烦:查找的结点四边八方都有点,答案可能出现在任意一个方向,不能无脑按x排序了
然后我们就想啊:要是当前结点为(x,y),其余的所有点都在(x,y)的左下角就好了
实际上我们是可以达到这种情况
因为我们只关心距离,所以我们可以通过各种奇怪的对称
关于x对称,关于y轴对称,关于原点对称

这样每个点都有机会变到(x,y)的左下角,要注意转换后的坐标一定要是正数

这样我们就把问题转化成了:对于一个点(x,y),计算其左下角的点中与ta曼哈顿距离最近的点
实际上:|x-x0|+|y-y0|=x-x0+y-y0=(x+y)-(x0+y0)

我们求的是左下角x+y的最大值

这样我们就可以用CDQ分治解决了
同样的,时间在输入的时候已经是有序的了
在分治的过程中,按照x坐标归并起来
最后把x+y的值扔进树状数组


口hu结束,我们看一下程序实现

  • 一开始读入的时候,(x,y)有可能有0值,但是我们要保证坐标必须是正数(方便点的对称),所以x++,y++
    同时我们需要记录一下最大坐标maxn (坐标为正数)
for (int i=1;i<=n;i++)
{scanf("%d%d",&po[i].x,&po[i].y); po[i].x++; po[i].y++;po[i].type=1; po[i].num=i; po[tot].ans=M*3;maxn=max(maxn,max(po[i].x,po[i].y));
}
m+=n;
for (int i=n+1;i<=m;i++)
{scanf("%d%d%d",&po[i].type,&po[i].x,&po[i].y); po[i].x++; po[i].y++;po[i].num=i; po[i].ans=N*3;maxn=max(maxn,max(po[i].x,po[i].y));
}
  • 在主程序中,我们要进行四次CDQ分治
sort(po+1,po+1+m,cmp);
maxn++;
CDQ(1,m);for (int i=1;i<=m;i++)a[i].x=-a[i].x+maxn;     //转化后的坐标不能为负数,关于y轴对称
sort(po+1,po+1+m,cmp);
CDQ(1,m);for (int i=1;i<=m;i++)a[i].y=-a[i].y+maxn;     //关于原点对称
sort(po+1,po+1+m,cmp);
CDQ(1,m);for (int i=1;i<=m;i++)a[i].x=-(a[i].x-maxn);   //关于x轴对称
sort(po+1,po+1+m,cmp);
CDQ(1,m);
  • 在归并的时候,我们先处理,再递归
  • 我们在记录修改的时候,记录了一个num,表示时间顺序
    所以在处理的时候需要按照num分成两部分,这样我们就可以挑出左区间的修改,维护右区间的询问
int t1=L,t2=M+1;
int maxx=0;                             //一开始我们按照x排序
for (int i=L;i<=R;i++)                  //现在按照时间分成前后两部分 if (po[i].num<=M) q[t1++]=po[i];else q[t2++]=po[i];
  • 在处理询问的时候,我们之前已经按照时间和x排过序了,现在要处理的是y坐标

我们选择的数据结构是树状数组,以y坐标为数组下标,x+y值为权值扔到树状数组中

树状数组不光能够计算前缀和,还可以计算前缀最大值

void clear(int x)
{for (int i=x;i<=maxn;i+=(i&(-i))) t[i]=-1;
}void change(int x,int z)
{for (int i=x;i<=maxn;i+=(i&(-i)))t[i]=max(t[i],z);
}int askmax(int x)
{int ans=-1;for (int i=x;i>0;i-=(i&(-i))) ans=max(ans,t[i]);return ans;
}

tip

第一次写这道题的时候,用的是KDtree
之后在问到曲神KDtree的空间复杂度时,
曲神和DP 严肃地 告诉我说:CDQ比较好

//这里写代码片
#include<cstring>
#include<iostream>
#include<cstdio>
#include<algorithm>using namespace std;const int N=1000010;
const int M=3000010;
struct node{int x,y,type,num,ans;bool operator <(const node &a) const{if (x!=a.x) return x<a.x;else return type<a.type;}
};
node po[N],q[N];
int n,m,tot=0,ans[N],totx=0,t[N],maxn=0;int cmp1(const node &a,const node &b)
{return a.num<b.num;
}int cmp(const node &a,const node &b)      //按照x坐标排序
{if (a.x!=b.x) return a.x<b.x;else return a.type<b.type;
}void clear(int x)
{for (int i=x;i<=maxn;i+=(i&(-i))) t[i]=-1;
}void change(int x,int z)
{for (int i=x;i<=maxn;i+=(i&(-i)))t[i]=max(t[i],z);
}int askmax(int x)
{int ans=-1;for (int i=x;i>0;i-=(i&(-i))) ans=max(ans,t[i]);return ans;
}void CDQ(int L,int R)
{if (L==1&&R==m) memset(t,-1,sizeof(t));if (L==R) return;int M=(L+R)>>1;int t1=L,t2=M+1;int maxx=0;                             //一开始我们按照x排序 for (int i=L;i<=R;i++)                  //现在按照时间分成前后两部分 if (po[i].num<=M) q[t1++]=po[i];else q[t2++]=po[i];for (int i=L;i<=R;i++) po[i]=q[i];t1=L,t2=M+1;int last=0; while (t2<=R)                           //处理询问 {while (t1<=M&&po[t1].type==2) t1++;while (t2<=R&&po[t2].type==1) t2++;if (t1<=M&&po[t1].x<=po[t2].x)      {change(po[t1].y,po[t1].x+po[t1].y);last=t1++;} else if (t2<=R){int t=askmax(po[t2].y);if (t!=-1)po[t2].ans=min(po[t2].ans,po[t2].x+po[t2].y-t);t2++;}}for (int i=L;i<=last;i++) if (po[i].type==1) clear(po[i].y);CDQ(L,M); CDQ(M+1,R);
}int main()
{scanf("%d%d",&n,&m);for (int i=1;i<=n;i++){scanf("%d%d",&po[i].x,&po[i].y); po[i].x++; po[i].y++;po[i].type=1; po[i].num=i; po[tot].ans=M*3;maxn=max(maxn,max(po[i].x,po[i].y));}m+=n;for (int i=n+1;i<=m;i++){scanf("%d%d%d",&po[i].type,&po[i].x,&po[i].y); po[i].x++; po[i].y++;po[i].num=i; po[i].ans=N*3;maxn=max(maxn,max(po[i].x,po[i].y));}sort(po+1,po+1+m,cmp);        maxn++;CDQ(1,m);for (int i=1;i<=m;i++)po[i].x=-po[i].x+maxn;      //转化后的坐标不能为负数sort(po+1,po+1+m,cmp);CDQ(1,m);for (int i=1;i<=m;i++)po[i].y=-po[i].y+maxn;sort(po+1,po+1+m,cmp);CDQ(1,m);for (int i=1;i<=m;i++)po[i].x=-(po[i].x-maxn);sort(po+1,po+1+m,cmp);CDQ(1,m);for (int i=1;i<=m;i++) po[i].y=-(po[i].y-maxn);sort(po+1,po+1+m,cmp1);for (int i=1;i<=m;i++) if (po[i].type==2)printf("%d\n",po[i].ans);return 0;
} 

bzoj2716 [Violet 3]天使玩偶(CDQ分治)相关推荐

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

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

  2. BZOJ 2716: [Violet 3]天使玩偶 | CDQ分治

    题目: 南开OJ有非权限提交处 http://oi.nks.edu.cn/zh/Problem/Details/2739 题解: 鹅鹅鹅....有三维(t,x,y),所以可以用CDQ解决的好题 初始点 ...

  3. bzoj2716 [Violet 3]天使玩偶

    2716: [Violet 3]天使玩偶 Time Limit: 80 Sec  Memory Limit: 128 MB Submit: 2160  Solved: 936 [Submit][Sta ...

  4. [KDTree] [BZOJ2716] [Violet 3] 天使玩偶

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

  5. bzoj2716 [Violet 3]天使玩偶(KDtree)

    题目描述 Ayu在七年前曾经收到过一个天使玩偶,当时她把它当作时间囊埋在了地下.而七年后 的今天,Ayu却忘了她把天使玩偶埋在了哪里,所以她决定仅凭一点模糊的记忆来寻找它. 我们把Ayu生活的小镇看作 ...

  6. 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]& ...

  7. BZOJ2648: SJY摆棋子2716: [Violet 3]天使玩偶

    BZOJ2648: SJY摆棋子 BZOJ2716: [Violet 3]天使玩偶 BZOJ氪金无极限... 其实这两道是同一题. 附上2648的题面: Description 这天,SJY显得无聊. ...

  8. BZOJ 2716: [Violet 3]天使玩偶

    2716: [Violet 3]天使玩偶 Time Limit: 80 Sec  Memory Limit: 128 MB Submit: 1473  Solved: 621 [Submit][Sta ...

  9. bzoj 2648: SJY摆棋子2716: [Violet 3]天使玩偶 --kdtree

    2648: SJY摆棋子&&2716: [Violet 3]天使玩偶 Time Limit: 20 Sec  Memory Limit: 128 MB Description 这天,S ...

最新文章

  1. 将HLSL射线追踪到Vulkan
  2. 函数依赖与 Armstrong公理
  3. [Java并发编程(二)] 线程池 FixedThreadPool、CachedThreadPool、ForkJoinPool?为后台任务选择合适的 Java executors...
  4. 【Win32汇编】数组累加
  5. 操作系统 第二章【记录型信号量机制、独木桥问题】【MOOC答案】
  6. 此地址使用了一个通常用于网络浏览以外的端口。出于安全原因,Firefox 取消了该请求...
  7. npm WARN build `npm build` called with no arguments. Did you mean to `npm run-script build`
  8. 查看表字段信息 sql,mysql,oracle
  9. Spring Boot jackson配置使用详解
  10. 信息学奥赛一本通(2049:【例5.19】字符串判等)
  11. Merry Christmas!看甜点海报如何庆祝圣诞
  12. 再次荣获最受观众喜爱奖
  13. openssl证书相关
  14. ElementUI:table获取复选中的数据
  15. 20140711 set
  16. session与cookie之间的关系
  17. winload.exe数字签名问题详解
  18. 等级保护三级和二级有什么区别?
  19. Three.js地球开发—1.经纬度转球面坐标
  20. ilo找不到服务器,云计算服务器忘记iLO登录账号的解决方法

热门文章

  1. CentOS7新特性——systemd及systemctl
  2. 计算机主板巨头,华硕/技嘉/微星三巨头近年主板业绩分析
  3. 使用jQuery开发一个超酷的倒计时效果
  4. 新手机第一次充电,怎么充电对电池好?
  5. PCL B样条曲线拟合(2d/3d)
  6. linux运行c语言pause,c++中的system(pause)的作用和含义解析
  7. 11月25日科技资讯|网易回应裁撤生病员工:确实存在简单粗暴不近人情行为
  8. Markdown插入视频、mp3音频和gif图的语法
  9. paramiko模块
  10. ajax同步和异步请求的区别