题目链接

考虑对于两个点a,b,距离为|x[a]-x[b]|+|y[a]-y[b]|,如果a在b的右上,那我们可以把绝对值去掉,即x[a]+y[a]-(x[b]+y[b])。

即我们要求满足x[b]<=x[a]且y[b]<=y[a]的最大的x[b]+y[b],用CDQ分治+树状数组解决。

那如果a不在b的右上呢?可以通过坐标变换解决(因为要求的只是相对距离)。

坐标变换可以用Xmax或Ymax减去xi或yi。

如果还用之前的方法,每次变换坐标前都要把操作序列变为初始序列(时间有序),但是这样很慢。。

不如在每次分治前按x sort一遍,分治中再以tm为关键字排序(同一区间内要时间有序)

优化: 遇到修改时才修改。所以先排好序再扫一遍更新答案

//43796kb	32744ms
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
#define lb(x) (x)&-(x)
const int N=5e5+5,MAXIN=2e6; int n,m,qcnt,acnt,Ans[N],A[N<<1];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Operation
{
int type,x,y,tm;
Operation() {}
Operation(int t,int _x,int _y,int _tm): type(t),x(_x),y(_y),tm(_tm) {}
bool operator <(const Operation &a)const{
return x==a.x?tm<a.tm:x<a.x;
}
}q[N<<1],tmp[N<<1]; namespace BIT
{
int Max,t[1000005];
void Update(int p,int v){
while(p<=Max) t[p]=std::max(t[p],v),p+=lb(p);
}
int Query(int p){
int res=0;
while(p) res=std::max(t[p],res),p^=lb(p);//^代替-
return res;
}
void Clear(int p){
while(p<=Max)//被这个Max坑半晚上。。
if(t[p]) t[p]=0,p+=lb(p);
else break;
}
}
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
void CDQ(int l,int r)
{
if(l<r)
{
int m=l+r>>1;
int p1=l,p2=m+1,t=0,val;
for(int i=l; i<=r; ++i)
if(q[i].tm<=m) tmp[p1++]=q[i];
else tmp[p2++]=q[i];
for(int i=l; i<=r; ++i) q[i]=tmp[i];//先排好 这样之后就可以只在查询时更新了(不需要排序了)
p1=l, p2=m+1;
for(; p2<=r; ++p2)
if(q[p2].type)
{
for(; p1<=m&&q[p1].x<=q[p2].x; ++p1)
if(!q[p1].type) A[++t]=q[p1].y,BIT::Update(q[p1].y,q[p1].x+q[p1].y);
if(val=BIT::Query(q[p2].y))
Ans[q[p2].type]=std::min(Ans[q[p2].type],q[p2].x+q[p2].y-val);//只在有点时更新答案! 否则答案会是它的x+y
}
// for(int i=l; i<p1; ++i)
// if(!q[i].type) BIT::Clear(q[i].y);
for(int i=1; i<=t; ++i) BIT::Clear(A[i]);
CDQ(l,m), CDQ(m+1,r);
}
} int main()
{
n=read(),m=read();
int Xmax=0,Ymax=0;
for(int x,y,i=1; i<=n; ++i)//非负坐标...
x=read()+1,y=read()+1,Xmax=std::max(Xmax,x),Ymax=std::max(Ymax,y),
q[++qcnt]=Operation(0,x,y,qcnt);
for(int t,x,y,i=1; i<=m; ++i)
t=read(),x=read()+1,y=read()+1,Xmax=std::max(Xmax,x),Ymax=std::max(Ymax,y),
q[++qcnt]=Operation(t==1?0:++acnt,x,y,qcnt);
++Xmax,++Ymax,BIT::Max=Ymax;
for(int i=1; i<=acnt; ++i) Ans[i]=Xmax+Ymax; std::sort(q+1,q+1+qcnt), CDQ(1,qcnt); for(int i=1; i<=qcnt; ++i) q[i].x=Xmax-q[i].x;
std::sort(q+1,q+1+qcnt), CDQ(1,qcnt); for(int i=1; i<=qcnt; ++i) q[i].y=Ymax-q[i].y;
std::sort(q+1,q+1+qcnt), CDQ(1,qcnt); for(int i=1; i<=qcnt; ++i) q[i].x=Xmax-q[i].x;
std::sort(q+1,q+1+qcnt), CDQ(1,qcnt); for(int i=1; i<=acnt; ++i) printf("%d\n",Ans[i]); return 0;
}

最初的代码(肯定是会T):

//懒得改了 看另一个吧。。
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
#define lb(x) (x)&-(x)
const int N=3e5+5; int n,m,qcnt,acnt,Ans[N];
struct Operation
{
int type,x,y;
Operation() {};
Operation(int t,int _x,int _y): type(t),x(_x),y(_y) {};
bool operator <(const Operation &a)const{
return x==a.x?type<a.type:x<a.x;
}
}q[N<<1],tmp[N<<1],A[N<<1]; namespace BIT
{
int Max,t[1000005];
void Update(int p,int v){
while(p<=Max) t[p]=std::max(t[p],v),p+=lb(p);
}
int Query(int p){
int res=0;
while(p) res=std::max(t[p],res),p-=lb(p);
return res;
}
void Clear(int p){
while(p<=Max)//被这个Max坑了半晚上。。
if(t[p]) t[p]=0,p+=lb(p);
else break;
}
}
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
void CDQ(int l,int r)
{//同理可以只在后半部分修改时更新前半部分 这个tm是有序的 结束后要按x归并使x有序
if(l<r)
{
int m=l+r>>1; CDQ(l,m), CDQ(m+1,r);
int p1=l,p2=m+1,t=0,val;
while(p1<=m&&p2<=r)
{
if(q[p1]<q[p2])
{
if(!q[p1].type) BIT::Update(q[p1].y,q[p1].x+q[p1].y);
tmp[t++]=q[p1++];
}
else
{
if(q[p2].type)
if(val=BIT::Query(q[p2].y))//只在有点时更新答案! 否则答案会是它的x+y
Ans[q[p2].type]=std::min(Ans[q[p2].type],q[p2].x+q[p2].y-val);
tmp[t++]=q[p2++];
}
}
if(p1<=m)
{
for(int i=l; i<p1; ++i)
if(!q[i].type) BIT::Clear(q[i].y);
while(p1<=m) tmp[t++]=q[p1++];
}
else
{
while(p2<=r)
{
if(q[p2].type)
if(val=BIT::Query(q[p2].y))
Ans[q[p2].type]=std::min(Ans[q[p2].type],q[p2].x+q[p2].y-val);
tmp[t++]=q[p2++];
}
for(int i=l; i<=m; ++i)
if(!q[i].type) BIT::Clear(q[i].y);
}
for(int i=0; i<t; ++i) q[l+i]=tmp[i];
}
} int main()
{
n=read(),m=read();
int Xmax=0,Ymax=0;
for(int x,y,i=1; i<=n; ++i)//非负坐标...
x=read()+1,y=read()+1,Xmax=std::max(Xmax,x),Ymax=std::max(Ymax,y),
q[++qcnt]=Operation(0,x,y);
for(int t,x,y,i=1; i<=m; ++i)
t=read(),x=read()+1,y=read()+1,Xmax=std::max(Xmax,x),Ymax=std::max(Ymax,y),
q[++qcnt]=Operation(t==1?0:++acnt,x,y);
++Xmax,++Ymax,BIT::Max=Ymax;
for(int i=1; i<=acnt; ++i) Ans[i]=Xmax+Ymax;
for(int i=1; i<=qcnt; ++i) A[i]=q[i]; CDQ(1,qcnt); for(int i=1; i<=qcnt; ++i) q[i]=A[i];
for(int i=1; i<=qcnt; ++i) q[i].x=Xmax-q[i].x;
CDQ(1,qcnt); for(int i=1; i<=qcnt; ++i) q[i]=A[i];
for(int i=1; i<=qcnt; ++i) q[i].y=Ymax-q[i].y;
CDQ(1,qcnt); for(int i=1; i<=qcnt; ++i) q[i]=A[i];
for(int i=1; i<=qcnt; ++i) q[i].x=Xmax-q[i].x,q[i].y=Ymax-q[i].y;
CDQ(1,qcnt); for(int i=1; i<=acnt; ++i) printf("%d\n",Ans[i]); return 0;
}

BZOJ.2716.[Violet3]天使玩偶(CDQ分治 坐标变换)的更多相关文章

  1. BZOJ.2716.[Violet3]天使玩偶(K-D Tree)

    题目链接 KD-Tree.因为插入过多点后可能会退化成链,所以左/右子树sz > α*整棵子树sz时对整棵子树进行重构. 树的节点数必须是3n?why?洛谷,BZOJ都这样..(数据范围错了吧 ...

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

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

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

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

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

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

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

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

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

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

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

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

  8. CH 4701 - 天使玩偶 - [CDQ分治]

    题目链接:传送门 关于CDQ分治(参考李煜东<算法竞赛进阶指南>): 对于一系列操作,其中的任何一个询问操作,其结果必然等价于:初始值 + 此前所有的修改操作产生的影响. 假设共有 $m$ ...

  9. 天使玩偶:CDQ分治

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

随机推荐

  1. android tools相关

    1.showin 在include 的根节点设置,可一预览效果

  2. 转----MarkdownPad2.5 注册码

    经测试可用 User: Soar360@live.com 授权: GBPduHjWfJU1mZqcPM3BikjYKF6xKhlKIys3i1MU2eJHqWGImDHzWdD6xhMNLGVpbP2 ...

  3. Python学习笔记5-时间模块time/datetime

    import time time.sleep(2) #等待几秒 # 1.格式化好的时间 2018-1-14 16:42 # 2.时间戳 是从unix元年到现在所有的秒数 # 3.时间元组 #想时间戳和 ...

  4. rsync更改端口后的同步办法

    rsync有两种常用的认证方式,一种为rsync-daemon方式,另外一种则是ssh. 在一些场合,使用rsync-daemon方式会比较缺乏灵活性,ssh方式则成为首选.但是今天实际操作的时候发现 ...

  5. swapper进程【转】

    转自:https://blog.csdn.net/qq_27357145/article/details/80462292 LINUX进程小结 id为0的进程通常是调度进程,常常被称为交换进程(swa ...

  6. XShell 使用方法

    XShell是一款Windows下非常优秀的远程连接Linux主机的工具,是平常使用不可缺少的工具.复制和粘贴由于在linux的Shell下,Ctrl+c是中断当前指令,这个快捷键和win系统下的复制 ...

  7. 浅谈js设计模式 — 享元模式

    享元(flyweight)模式是一种用于性能优化的模式,“fly”在这里是苍蝇的意思,意为蝇量级.享元模式的核心是运用共享技术来有效支持大量细粒度的对象. 假设有个内衣工厂,目前的产品有 50种男式内 ...

  8. Excel学习笔记:vlookup基础及多条件查找

    一.vlookup基础 关于vlookup的基础不多记录,相信基本的使用方法大家都懂得. 使用格式:=vlookup(搜索值,搜索范围,列号,是否精准匹配) =VLOOKUP(E2,$B$2:$C$6 ...

  9. watch案例解析(element-ui el-select 无法选中问题剖析)

    fire 读在最前面: 1.此文章衔接Vue 虚拟Dom 及 部分生命周期初探,相关整体知识点请先阅读后再继续本文阅读 问:子组件中明明有watch value,为什么this.$emit('inpu ...

  10. Kosaraju算法学习

    Kosaraju 算法学习 序 这星期捣鼓了一个新的算法--Kosaraju算法 今天分享给大家 简介 Kosaraju算法,其实与tarjan算法差不多.但是码量较小,容易记忆.其时间复杂度与tar ...