jzoj2866. 【集训队互测 2012】Bomb

Description

A 国和 B 国是两个超级大国,长期处于冷战状态;
A 国在 B 国中设有 N 个情报站,编号为 1,2,3, …… ,N ,每个情报站有一个坐标 (Xi,Yi) 。但是, A 国的工作人员发现,每个情报站里都被埋上了炸弹!
这些炸弹非常特殊 , 只要同时拆除其中的三个炸弹 , 所有炸弹就都不会爆炸了。
由于各个情报站联络需要代价 , 拆除炸弹需要花费的总代价为这些炸弹两两之间的曼哈顿距离和。
现在 A 国的指挥部门找到了你 , 希望知道可能需要的最大代价和最小代价 。

Input

输入的第一行包含一个整数 N 。接下来 N 行,第 i+1 行两个整数 Xi,Yi ,表示第 i 个情报站的坐标。

Output

输出两行 , 每行包含一个整数 , 第一行表示可能的最大代价 , 第二行表示可能的最小代价。

Sample Input

4
1 1
1 2
2 1
2 3

Sample Output

6
4

Data Constraint

对于 30% 的数据, N<=500
对于另外 10% 的数据,每个点出现至少两遍
对于 50% 的数据, N<=1000
对于 60% 的数据, N<=8000
对于 70% 的数据, N<=15000
对于 80% 的数据, N<=50000
对于 100% 的数据, N<=100000 , 0<=Xi,Yi<=10^8

Hint

对于两个点 (X0,Y0),(X1,Y1) ,
它们之间的曼哈顿距离为 abs(X0-X1)+abs(Y0-Y1) 。

赛时

这道题比较奇妙。
一开始我还以为是什么计算几何神仙题。
结果推一推发现不是。
无脑选手++
最后交了个30分暴力滚粗。

题解

话说这道题数据有点水,可以利用 n 2 n^2 n2卡到90分。
然后利用分段打表两者相结合就过了。
无话可说

100%
我们首先考虑最大值怎么求:
我们发现,一个点对答案做出贡当且仅当其作为Xmin,Xmax,Ymin,Ymax出现才行。
然后贪心做即可。

然后就是最小值怎么求:
我们首先把整个图形旋转90旋转4次,每次就会出现以下两种情况可能对答案有贡献:
jzoj2866. 【集训队互测 2012】Bomb_第1张图片
jzoj2866. 【集训队互测 2012】Bomb_第2张图片
对于第一种,我们直接枚举中间的点,然后找出其右上方向离它最近的点,和左下方向离它最近的点。
更新答案即可。
但是一定要注意到重复出现的点。

对于第二种,做法就比较神奇了。
我们看到上图,可以发现,最左下的点提供Xmin,Ymin
最上面的点提供Ymax,最右边的点提供Xmax。
那么我们只需要快速求出后面两个即可。

考虑扫描线,首先当然是离散化y轴啦~
我们考虑从右往左边扫。
在这个y轴建立一个线段树,线段树上每个点记录的是:
x:当前行到扫到的列最近的点的x坐标。
y:在当前行上距离当前行最近的点的y坐标。
ans:当前点作为最右边的点的答案。
那么每次扫到一个点(xi,yi),我们就在线段树上求出它上面的点中答案最小的点。
然后更新。
首先,把当前点所在的行的x更新成xi
然后把当前点下面的行的y都打上lazy标记。
每次下传标记时我们就判断lazy标记是否能更新y值(lazy 实现起来细节还是比较多的(尤其是标记下传)

标程

小细节真™多。

#include 
#include 
#include 
using namespace std;

struct node
{
	long long xmin,ymin,ans;
};

int n;
long long maxans,minans,ans,pp;
long long x[100010],y[100010],ny[100010],id[100010];
long long lazy[400010],t[400010],op[100010][5],gb[400010],xx[100010],yy[100010];
node tree[400010],gbt[400010];

int abs(int x)
{
	if (x<0) return -x;
	else return x;
}

void qsort(int l,int r)
{
	int i=l;int j=r;
	int m=xx[(i+j)/2];
	int m1=yy[(i+j)/2];
	int m2=id[(i+j)/2];
	while (i<=j)
	{
		while ((xx[i]>m)||(xx[i]==m && yy[i]>m1)||(xx[i]==m && yy[i]==m1 && ((pp==0 && id[i]>m2)||(pp==1 && id[i]<m2)))) i++;
		while ((xx[j]<m)||(xx[j]==m && yy[j]<m1)||(xx[j]==m && yy[j]==m1 && ((pp==0 && id[j]<m2)||(pp==1 && id[j]>m2)))) j--;
		if (i<=j)
		{
			swap(xx[i],xx[j]);
			swap(yy[i],yy[j]);
			swap(ny[i],ny[j]);
			swap(id[i],id[j]);
			i++;j--;
		}
	}
	if (l<j) qsort(l,j);
	if (r>i) qsort(i,r); 
}

void qsorty(int l,int r)
{
	int i=l;int j=r;
	int m=yy[(i+j)/2];
	while (i<=j)
	{
		while (yy[i]<m) i++;
		while (yy[j]>m) j--;
		if (i<=j)
		{
			swap(yy[i],yy[j]);
			swap(xx[i],xx[j]);
			swap(id[i],id[j]);
			i++;j--;
		}
	}
	if (l<j) qsorty(l,j);
	if (r>i) qsorty(i,r); 
}

void lazy_down(int x)
{
	tree[x*2].ymin=min(tree[x*2].ymin,lazy[x]);
	tree[x*2+1].ymin=min(tree[x*2+1].ymin,lazy[x]);
	lazy[x*2]=min(lazy[x*2],lazy[x]);
	lazy[x*2+1]=min(lazy[x*2+1],lazy[x]);
	tree[x*2].ans=min(tree[x*2].ans,tree[x*2].xmin+lazy[x]); 
	tree[x*2+1].ans=min(tree[x*2+1].ans,tree[x*2+1].xmin+lazy[x]);
	lazy[x]=1000000000;
}

void queryall(int x,int l,int r,int st,int en)
{
	if (l==st && r==en)
	{
		ans=min(ans,tree[x].ans);
	}
	else
	{
		int mid=(l+r)/2;
		lazy_down(x);
		if (en<=mid) queryall(2*x,l,mid,st,en);
		else if (st>mid) queryall(2*x+1,mid+1,r,st,en);
		else
		{
			queryall(2*x,l,mid,st,mid);
			queryall(2*x+1,mid+1,r,mid+1,en);
		}		
	}
}

void find(int x,int l,int r,int st,int en)
{
	if (l==st && r==en)
	{
		ans=min(ans,t[x]);
	}
	else
	{
		int mid=(l+r)/2;
		if (en<=mid) find(2*x,l,mid,st,en);
		else if (st>mid) find(2*x+1,mid+1,r,st,en);
		else
		{
			find(2*x,l,mid,st,mid);
			find(2*x+1,mid+1,r,mid+1,en);
		}		
	}
}

void change(int x,int l,int r,int st,int en)
{
	if (l==r)
	{
		t[x]=en;
	}
	else
	{
		int mid=(l+r)/2;
		if (st<=mid) change(2*x,l,mid,st,en);
		else if (st>mid) change(2*x+1,mid+1,r,st,en);	
		t[x]=min(t[2*x],t[2*x+1]);
	}
}

void modifyx(int x,int l,int r,int st,int en)
{
	if (l==r)
	{
		tree[x].xmin=en;
	}
	else
	{
		int mid=(l+r)/2;
		lazy_down(x);
		if (st<=mid) modifyx(2*x,l,mid,st,en);
		else if (st>mid) modifyx(2*x+1,mid+1,r,st,en);	
		tree[x].xmin=min(tree[2*x].xmin,tree[2*x+1].xmin);
	}
}

void modifyy(int x,int l,int r,int st,int en,long long op)
{
	if (l==st && r==en)
	{
		if (tree[x].ymin>op)
		{
			tree[x].ymin=op;
		}		
		lazy[x]=min(lazy[x],op);
		tree[x].ans=min(tree[x].ans,tree[x].xmin+op);
	}
	else
	{
		int mid=(l+r)/2;
		lazy_down(x);
		if (en<=mid) modifyy(2*x,l,mid,st,en,op);
		else if (st>mid) modifyy(2*x+1,mid+1,r,st,en,op);
		else
		{
			modifyy(2*x,l,mid,st,mid,op);
			modifyy(2*x+1,mid+1,r,mid+1,en,op);
		}	
		tree[x].ymin=min(tree[2*x].ymin,tree[2*x+1].ymin);
		tree[x].ans=min(tree[2*x].ans,tree[2*x+1].ans);	
	}
}

void find_min(int now)
{
	for (int i=1;i<=n;i++)
	{
		id[i]=i;
	}
	qsorty(1,n);
	int j=1;
	ny[1]=1;
	for (int i=2;i<=n;i++)
	{
		if (yy[i]!=yy[i-1])
		{
			j++;
		}
		ny[i]=j;
	}
	int zd=j;
	qsort(1,n);
	memcpy(tree,gbt,sizeof(gbt));
	memcpy(lazy,gb,sizeof(gb));
	memcpy(t,gb,sizeof(gb));
	for (int i=1;i<=n;i++)
	{
		ans=1000000000;
		find(1,1,zd,ny[i],zd);
		op[id[i]][now]=min(op[id[i]][now],ans-xx[i]-yy[i]);
		change(1,1,zd,ny[i],xx[i]+yy[i]);
		ans=1000000000;
		if (i>=3)
		if (ny[i]<zd)
		queryall(1,1,zd,ny[i]+1,zd);
		minans=min(minans,ans-xx[i]-yy[i]);
		modifyx(1,1,zd,ny[i],xx[i]);
		if (ny[i]>1)
		modifyy(1,1,zd,1,ny[i]-1,yy[i]);
	}
}

void find_max()
{
	long long xmin=1000000000;
	long long ymin=1000000000;
	long long xmax=-1000000000;
	long long ymax=-1000000000;
	for (int i=1;i<=n;i++)
	{
		xmin=min(xmin,xx[i]);
		xmax=max(xmax,xx[i]);
		ymin=min(ymin,yy[i]);
		ymax=max(ymax,yy[i]);
	}
	for (int i=1;i<=n;i++)
	{
		maxans=max(maxans,2*(max(xx[i]-xmin,xmax-xx[i])+max(yy[i]-ymin,ymax-yy[i])));
	}
}

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		scanf("%d%d",&x[i],&y[i]);
	}
	maxans=-1000000000;
	minans=1000000000;
	for (int i=0;i<400000;i++) gb[i]=1000000000;
	for (int i=0;i<400000;i++)
	{
		gbt[i].ans=1000000000;
		gbt[i].xmin=1000000000;
		gbt[i].ymin=1000000000;
	}
	memset(op,127,sizeof(op));
	memcpy(xx,x,sizeof(x));
	memcpy(yy,y,sizeof(y));
	find_max();
	for (int i=1;i<=4;i++)
	{
		memcpy(xx,x,sizeof(x));
		memcpy(yy,y,sizeof(y));
		if (i==1) find_min(i);
		else if (i==2)
		{
			for (int j=1;j<=n;j++)
			{
				swap(xx[j],yy[j]);
				yy[j]+=100000000;
				xx[j]=100000000-xx[j];
			}
			find_min(i);
		}
		else
		if (i==3)
		{
			pp++;
			for (int j=1;j<=n;j++)
			{
				xx[j]=6-xx[j];
				yy[j]=6-yy[j];
			}
			find_min(i);
		}
		else
		{
			for (int j=1;j<=n;j++)
			{
				swap(xx[j],yy[j]);
				yy[j]=100000000-yy[j];
				xx[j]+=100000000;
			}
			find_min(i);
		}
	}
	for (int i=1;i<=n;i++)
	{
		minans=min(minans,op[i][1]+op[i][3]);
		minans=min(minans,op[i][2]+op[i][4]);
	}
	printf("%d\n",maxans);
	printf("%d\n",minans*2);
}

你可能感兴趣的:(线段树&树状数组&权值线段树,扫描线)