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

\(\Large\natural\) P4169 [Violet]天使玩偶,SJY摆棋子 \ 原题链接

题意

由于特殊原因,加了个题意简述

一开始有 \(n\) 个关键点 \((a,b)\)。然后有 \(T\) 个操作:

  • 多出一个关键点 \((a,b)\)
  • 询问如果我站在 \((x,y)\),最近的关键点距离我多远。(这里的距离是曼哈顿距离

\(n,T\le 3\times 10^5\),以及 \(0\le a,b,x,y\le 10^6\)

解法

一开始的 \(n\) 个点直接当修改操作。

曼哈顿距离表示是 \(|x_1-x_2|+|y_1-y_2|\),这显然不好算。一般对于曼哈顿距离有两种解决方案,一种是转成切比雪夫距离,另一种是拆绝对值。我们看到这题有修改操作,想到拆绝对值更方便一些。

对于询问操作,我们分四种情况讨论(最近的点在我的左上 / 右上 / 左下 / 右下)。看到题目有三个维度 \(x,y,t\),我们可以想到 CDQ分治


下面以点在我右上角为例。

对于每一个询问 \((x,y)\),其它点处我的距离是:

\[|x-a|+|y-b|=a-x+b-y=a+b-x-y \]

\(x,y\) 是不会变的,我们只用找到 \(a+b\) 的最小值即可。

按照 CDQ分治 的套路,我们已经把区间一分为二。对于每个区间,我们以 \(x\) 为第一关键字, \(y\) 为第二关键字排序操作。

我们的目的是,查询 \((x,y)\) 右上角的点中 \(a+b\) 最小值,我们可以用线段树维护。

然后每次把 \(a\ge x\) 的修改都加入(给 \(b\) 这个位置更新 \(a+b\) 权值)。这个时候所有的关键点肯定都在 \((x,y)\)右边。而查询 \(y\sim \inf\) 就保证了关键点在 \((x,y)\)上方,所以就查询到了右上角啦~

最后根据套路,记得清空线段树


其它三个方位都是类似的,只用稍微改一下排序方式、枚举方式、线段树更新权值、线段树查询区间就行了。看似很复杂,但只要多在纸上画画图就可以清晰地打完题目了。

代码

注意因为一开始的关键点都当修改操作了,所以存操作的数组空间得开两倍

数组要清空为极大值。

#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
const int n7=601234,t7=4012345,hug=1000000;
struct dino{int sys,x,y,id;}qq[n7];
int n,T,tre[t7],ans[n7];

int rd(){
    int shu=0;char ch=getchar();
    while( !isdigit(ch) )ch=getchar();
    while( isdigit(ch) )shu=(shu<<1)+(shu<<3)+(ch^48),ch=getchar();
    return shu;
}

void updat(int o,int l,int r,int id,int x){
	if(l==r){tre[o]=x;return;}
	int mid=(l+r)>>1;
	if(id<=mid)updat(o<<1,l,mid,id,x);
	else updat(o<<1|1,mid+1,r,id,x);
	tre[o]=min(tre[o<<1],tre[o<<1|1]);
} 

int query(int o,int l,int r,int L,int R){
	if(L<=l&&r<=R)return tre[o];
	int mid=(l+r)>>1,fin=INT_MAX;
	if(L<=mid)  fin=query(o<<1,l,mid,L,R);
	if(R>=mid+1)fin=min(fin, query(o<<1|1,mid+1,r,L,R) );
	return fin;
}

bool cmp1(dino p,dino q){return p.x==q.x?p.y>q.y:p.x>q.x;}

bool cmp2(dino p,dino q){return p.x==q.x?p.y>q.y:p.x<q.x;}

bool cmp3(dino p,dino q){return p.x==q.x?p.y<q.y:p.x<q.x;}

bool cmp4(dino p,dino q){return p.x==q.x?p.y<q.y:p.x>q.x;}

void CDQ(int l,int r){
	if(l==r||r<=n)return;
	int mid=(l+r)>>1,L;
	CDQ(l,mid),CDQ(mid+1,r);

	//点在我站位右上角 
	sort(qq+l,qq+mid+1,cmp1);
	sort(qq+mid+1,qq+r+1,cmp1);
	L=l;
	rep(i,mid+1,r){
		if(qq[i].sys==1)continue;
		while(qq[L].x>=qq[i].x&&L<=mid){
			if(qq[L].sys==1)updat(1,0,hug,qq[L].y,qq[L].x+qq[L].y);
			L++;
		}
		ans[ qq[i].id ]=min(ans[ qq[i].id ],query(1,0,hug,qq[i].y,hug)-qq[i].x-qq[i].y);
	}
	rep(i,l,L-1){
		if(qq[i].sys==1)updat(1,0,hug,qq[i].y,tre[0]);
	}
	
	//点在我站位左上角 
	sort(qq+l,qq+mid+1,cmp2);
	sort(qq+mid+1,qq+r+1,cmp2);
	L=l;
	rep(i,mid+1,r){
		if(qq[i].sys==1)continue;
		while(qq[L].x<=qq[i].x&&L<=mid){
			if(qq[L].sys==1)updat(1,0,hug,qq[L].y,-qq[L].x+qq[L].y);
			L++;
		}
		ans[ qq[i].id ]=min(ans[ qq[i].id ],query(1,0,hug,qq[i].y,hug)+qq[i].x-qq[i].y);
	}
	rep(i,l,L-1){
		if(qq[i].sys==1)updat(1,0,hug,qq[i].y,tre[0]);
	}
	
	//点在我站位左下角 
	sort(qq+l,qq+mid+1,cmp3);
	sort(qq+mid+1,qq+r+1,cmp3);
	L=l;
	rep(i,mid+1,r){
		if(qq[i].sys==1)continue;
		while(qq[L].x<=qq[i].x&&L<=mid){
			if(qq[L].sys==1)updat(1,0,hug,qq[L].y,-qq[L].x-qq[L].y);
			L++;
		}
		ans[ qq[i].id ]=min(ans[ qq[i].id ],query(1,0,hug,0,qq[i].y)+qq[i].x+qq[i].y);
	}
	rep(i,l,L-1){
		if(qq[i].sys==1)updat(1,0,hug,qq[i].y,tre[0]);
	}
	
	//点在我站位右下角 
	sort(qq+l,qq+mid+1,cmp4);
	sort(qq+mid+1,qq+r+1,cmp4);
	L=l;
	rep(i,mid+1,r){
		if(qq[i].sys==1)continue;
		while(qq[L].x>=qq[i].x&&L<=mid){
			if(qq[L].sys==1)updat(1,0,hug,qq[L].y,qq[L].x-qq[L].y);
			L++;
		}
		ans[ qq[i].id ]=min(ans[ qq[i].id ],query(1,0,hug,0,qq[i].y)-qq[i].x+qq[i].y);
	}
	rep(i,l,L-1){
		if(qq[i].sys==1)updat(1,0,hug,qq[i].y,tre[0]);
	}
}

int main(){
	n=rd(),T=rd();
	rep(i,1,n)qq[i]=(dino){1,rd(),rd(),0};
	rep(i,n+1,n+T)qq[i]=(dino){rd(),rd(),rd(),i-n};
	memset(ans,0x3f,sizeof ans);
	memset(tre,0x3f,sizeof tre);
	CDQ(1,n+T);
	rep(i,1,T){
		if(ans[i]^ans[0])printf("%d\n",ans[i]);
	}
	return 0;
}

posted @ 2021-03-09 20:25  BlankAo  阅读(87)  评论(0编辑  收藏  举报