【洛谷P4169】[Violet]天使玩偶/SJY摆棋子

题目

题目链接:https://www.luogu.com.cn/problem/P4169
Ayu 在七年前曾经收到过一个天使玩偶,当时她把它当作时间囊埋在了地下。而七年后的今天,Ayu 却忘了她把天使玩偶埋在了哪里,所以她决定仅凭一点模糊的记忆来寻找它。
我们把 Ayu 生活的小镇看作一个二维平面坐标系,而 Ayu 会不定时地记起可能在某个点 \((x,y)\) 埋下了天使玩偶;或者 Ayu 会询问你,假如她在 \((x,y)\) ,那么她离近的天使玩偶可能埋下的地方有多远。
因为 Ayu 只会沿着平行坐标轴的方向来行动,所以在这个问题里我们定义两个点之间的距离为 \(dist(A,B)=|A_x-B_x|+|A_y-B_y|\)。其中 \(A_x\) 表示点 \(A\) 的横坐标,其余类似。

思路

这个绝对值很烦,考虑分类讨论,分别求出娃娃位于 Ayu 左上、左下、右上、右下的最短距离。
以左上为例。当娃娃位于 Ayu 左上方时,设 Ayu 位于\((x,y)\),娃娃位于\((p,q)\),那么有\(|x-p|+|y-q|=x-y-(p-q)\)。我们要让这个玩意最小。
因为\(x-y\)是固定的,所以我们只要让\(p-q\)尽量大就行。
那么我们将询问和修改都看作一个四元组\((t,x,y,opt)\),其中\(t\)是时间戳,\((x,y)\)是娃娃或 Ayu 的位置,\(opt\)表示这是询问还是修改。
那么我们对于每一个询问\((t_i,x_i,y_i,opt_i=2)\),我们需要在满足\(_j<t_i,x_j\leq x_i,y_j\geq y_i,opt=1\)的所有的\(j\)中查找\(x_j+y_j\)最大的。这显然是一个三维偏序,只不过将个数改成了最大值。
那么就用与三维偏序基本相同的方法可以解决。
做完左上之后,仍然要做好左下、右上、右下。然后在其中求最小值。
我们发现,我们把整个坐标系顺时针旋转\(90\)°就可以将原来位于\((x,y)\)左上的点变到\((x,y)\)右上方。所以我么只要旋转3次,求4次答案即可。

这道题需要一些卡常。

  1. 加Ofast,inline
  2. 将每次合并时的分别将两个区间\(sort\)改为归并。
  3. 每次旋转之后需要将所有四元组重新按照时间戳排序,可以在最开始开一个数组,每次直接复制过去就可以了
  4. 如果不使用\((3)\)的方法,可以重载\(node\)的小于运算符,这样不用写\(cmp\),会快一些的。
  5. 不要使用线段树来求区间最值,常数过大。可以使用树状数组来求。

代码

#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")  //不会卡常啊,只能用物理性卡常了
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=300010,M=1000010,Inf=1e9;
int n,m,maxn;

inline int read()
{
	int d=0,f=1; char ch=getchar();
	while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
	while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
	return d*f;
}

struct node
{
	int t,x,y,ans,opt;
}a[N*2],b[N*2];

inline bool operator < (const node &a,const node &b)
{
    return a.t<b.t;
}

struct BIT
{
	int c[M];
	
	inline void update(int x,int val)
	{
		if (!x) return;
		for (;x<=maxn;x+=x&-x)
			c[x]=max(c[x],val);
	}
	
	inline int ask(int x)
	{
		int ans=0;
		for (;x;x-=x&-x)
			ans=max(ans,c[x]);
		return ans;
	}
	
	inline void clear(int x)
	{
		for (;x<=maxn && c[x];x+=x&-x)
			c[x]=0;
	}
}bit;

inline void spin()
{
	for (register int i=1;i<=n+m;i++)
	{
		swap(a[i].x,a[i].y);
		a[i].y=M-9-a[i].y;
		maxn=max(maxn,max(a[i].x,a[i].y));
	}
}

inline void cdq(int l,int r)  //上文是以左上为例,实际写的是左下,相对好些一些。
{
	if (l==r) return;
	int mid=(l+r)>>1,i=l,mn=Inf;
	cdq(l,mid); cdq(mid+1,r);
	for (register int j=mid+1;j<=r;j++)
	{
		if (a[j].opt==1) continue;
		for (;a[i].x<=a[j].x && i<=mid;i++)
			if (a[i].opt==1)
			{
				bit.update(a[i].y,a[i].x+a[i].y);
				if (a[i].y<mn) mn=a[i].y;
			}
		if (mn<=a[j].y)
			a[j].ans=min(a[j].ans,a[j].x+a[j].y-bit.ask(a[j].y));
	}
	for (register int j=l;j<i;j++)
		if (a[j].opt==1) bit.clear(a[j].y);
	int p=l,q=mid+1,tot=0;
	for (;p<=mid && q<=r;)
		if (a[p].x<a[q].x) b[++tot]=a[p],p++;
			else b[++tot]=a[q],q++;
	for (;p<=mid;p++) b[++tot]=a[p];
	for (;q<=r;q++) b[++tot]=a[q];
	for (int j=l;j<=r;j++)
		a[j]=b[j-l+1];
}

int main()
{
	n=read(); m=read();
	for (register int i=1;i<=n;i++)
	{
		a[i].x=read(); a[i].y=read(); 
		a[i].t=i; a[i].opt=1;
		maxn=max(maxn,max(a[i].x,a[i].y));
	}
	for (register int i=n+1;i<=n+m;i++)
	{
		a[i].opt=read(); a[i].x=read(); a[i].y=read(); 
		a[i].t=i; a[i].ans=Inf;
		maxn=max(maxn,max(a[i].x,a[i].y));
	}
	sort(a+1,a+1+n+m); cdq(1,n+m); spin();
	sort(a+1,a+1+n+m); cdq(1,n+m); spin();
	sort(a+1,a+1+n+m); cdq(1,n+m); spin();
	sort(a+1,a+1+n+m); cdq(1,n+m);
	sort(a+1,a+1+n+m);
	for (register int i=1;i<=n+m;i++)
		if (a[i].opt==2) printf("%d\n",a[i].ans);
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/12228355.html