清橙A1322. Bomb

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

虽然题目本身不难(然而我自己菜并没有想出怎么做),但是写的时候还是有点懵逼。

最大值的求法很简单,下面只考虑求最小值

实际上哈密顿路径之和就是包含了这三个点的最小矩形的周长。分两种情况,一种是其中两个点分别确定两个角,另一种是一个点确定一个角,另两个分别确定两条边。

对于第一种情况枚举中间这个点,对于第二种情况枚举角上的那个点,用线段树维护即可。顺序很重要,运用修改和查询的特性进行优化,很容易晕。

最后要注意的一点是,如果有点重复了的话,要特殊处理。

基本上就是个细节题吧。

#include<bits/stdc++.h>
using namespace std;

int ti;


#define mid ((l+r)>>1)
const int maxn=100100;
const int INF=1e9;
void chkmax(int &x,int y){
	x=x>y?x:y;
}
void chkmin(int &x,int y){
	x=x<y?x:y;
}
int Max(int x,int y){
	return x>y?x:y;
}
int Abs(int x){
	return x>0?x:-x;
}
int n;
struct node{
	int x,y,id;
}a[maxn];
bool operator != (const node &A,const node &B){
	return A.x!=B.x||A.y!=B.y;
}
bool cmp(const node &A,const node &B){
	return A.y==B.y?A.x<B.x:A.y<B.y;
}
void solve_max(){
	int maxx=-INF,maxy=-INF,minx=INF,miny=INF;
	for(int i=1;i<=n;i++){
		chkmax(maxx,a[i].x),chkmax(maxy,a[i].y);
		chkmin(minx,a[i].x),chkmin(miny,a[i].y);
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		chkmax(ans,Abs(a[i].x-minx)+Abs(a[i].y-miny));
		chkmax(ans,Abs(a[i].x-maxx)+Abs(a[i].y-maxy));
		chkmax(ans,Abs(a[i].x-minx)+Abs(a[i].y-maxy));
		chkmax(ans,Abs(a[i].x-maxx)+Abs(a[i].y-miny));
	}
	printf("%d\n",ans*2);
}
int b[maxn];
namespace Min_normal{
	struct Tree{
		int x,y,ans;
	}tr[maxn<<2];
	void newnode(int h){
		tr[h].x=-INF,tr[h].y=-INF,tr[h].ans=-INF;
	}
	void check(int h,int x){
		if(x>tr[h].x){
			tr[h].x=x;
			chkmax(tr[h].ans,b[tr[h].x]+tr[h].y);
		}
	}
	void push_down(int h){
		if(tr[h].x<=-INF) return;
		check(h<<1,tr[h].x);
		check(h<<1|1,tr[h].x);
		tr[h].x=-INF;
	}
	void push_up(int h){
		chkmax(tr[h].y,tr[h<<1].y);
		chkmax(tr[h].y,tr[h<<1|1].y);
		chkmax(tr[h].ans,tr[h<<1].ans);
		chkmax(tr[h].ans,tr[h<<1|1].ans);
	}
	void Build_tree(int h,int l,int r){
		newnode(h);
		if(l==r) return;
		Build_tree(h<<1,l,mid);
		Build_tree(h<<1|1,mid+1,r);
	}
	void updataa(int h,int l,int r,int s,int t,int x){
		if(s<=l&&r<=t){
			check(h,x);
			return;
		}
		push_down(h);
		if(t<=mid) updataa(h<<1,l,mid,s,t,x);
		else if(s>mid) updataa(h<<1|1,mid+1,r,s,t,x);
		else{
			updataa(h<<1,l,mid,s,mid,x);
			updataa(h<<1|1,mid+1,r,mid+1,t,x);
		}
		push_up(h);
	}
	void updatab(int h,int l,int r,int p,int v){
		if(l==r){
			tr[h].y=v,tr[h].x=-INF;
			return;
		}
		push_down(h);
		if(p<=mid) updatab(h<<1,l,mid,p,v);
		else updatab(h<<1|1,mid+1,r,p,v);
		push_up(h);
	}
	int Query(int h,int l,int r,int s,int t){
		if(s<=l&&r<=t)
			return tr[h].ans;
		push_down(h);
		if(t<=mid) return Query(h<<1,l,mid,s,t);
		else if(s>mid) return Query(h<<1|1,mid+1,r,s,t);
		else
			return Max(Query(h<<1,l,mid,s,mid),Query(h<<1|1,mid+1,r,mid+1,t));
	}
	int solve(){
		for(int i=1;i<=n;i++)
			b[i]=a[i].x;
		sort(b+1,b+n+1);
		for(int i=1;i<=n;i++)
			a[i].x=lower_bound(b+1,b+n+1,a[i].x)-b;
		sort(a+1,a+n+1,cmp);
		Build_tree(1,1,n);
		int ans=INF;
		for(int i=1;i<=n;i++){
			ti=i;
			chkmin(ans,b[a[i].x]+a[i].y-Query(1,1,n,1,a[i].x));
			updataa(1,1,n,a[i].x,n,a[i].x);
			updatab(1,1,n,a[i].x,a[i].y);
		}
		for(int i=1;i<=n;i++)
			a[i].x=b[a[i].x];
		return ans;
	}
}
namespace Min_extra{
	int sum1[maxn],sum2[maxn];
	int tr[maxn];
	void clear(){
		for(int i=1;i<=n;i++)
			tr[i]=-INF;
	}
	int Query(int x){
		int res=-INF;
		while(x){
			chkmax(res,tr[x]);
			x-=x&(-x);
		}
		return res;
	}
	void updata(int x,int v){
		while(x<=n){
			chkmax(tr[x],v);
			x+=x&(-x);
		}
	}
	void solve_mid(int *sum){
		for(int i=1;i<=n;i++)
			b[i]=a[i].x;
		sort(b+1,b+n+1);
		for(int i=1;i<=n;i++)
			a[i].x=lower_bound(b+1,b+n+1,a[i].x)-b;
		sort(a+1,a+n+1,cmp);
		clear();
		for(int i=1;i<=n;i++){
			sum[a[i].id]=b[a[i].x]+a[i].y-Query(a[i].x);
			updata(a[i].x,b[a[i].x]+a[i].y);
		}
		for(int i=1;i<=n;i++)
			a[i].x=b[a[i].x];
	}
	int solve(){
		int ans=INF;
		solve_mid(sum1);
		for(int i=1;i<=n;i++)
			a[i].x=-a[i].x,a[i].y=-a[i].y;
		solve_mid(sum2);
		for(int i=1;i<=n;i++)
			chkmin(ans,sum1[i]+sum2[i]);
		return ans;
	}
}
namespace Special{
	bool ok[maxn];
	int sum[maxn];
	int Unique(){
		int res=INF;
		sort(a+1,a+n+1,cmp);
		int cnt=0,num=0;
		for(int i=1;i<=n;i++){
			if(i==1||a[i]!=a[num]) a[++num]=a[i],cnt=1;
			else cnt++;
			if(cnt==2) ok[a[num].id]=1;
			if(cnt>=3) return 0;
		}
		n=num;
		Min_extra::solve_mid(sum);
		for(int i=1;i<=n;i++){
			a[i].x=-a[i].x;
			if(ok[a[i].id]) chkmin(res,sum[a[i].id]);
		}
		Min_extra::solve_mid(sum);
		for(int i=1;i<=n;i++){
			a[i].y=-a[i].y;
			if(ok[a[i].id]) chkmin(res,sum[a[i].id]);
		}
		Min_extra::solve_mid(sum);
		for(int i=1;i<=n;i++){
			a[i].x=-a[i].x;
			if(ok[a[i].id]) chkmin(res,sum[a[i].id]);
		}
		Min_extra::solve_mid(sum);
		for(int i=1;i<=n;i++){
			a[i].y=-a[i].y;
			if(ok[a[i].id]) chkmin(res,sum[a[i].id]);
		}
		for(int i=1;i<=n;i++)
			a[i].id=i;
		return res;
	}
}
int main(){
//	freopen("A1322.in","r",stdin);
//	freopen("A1322.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d%d",&a[i].x,&a[i].y);
		a[i].id=i;
	}
	solve_max();
	int ans=INF;
	chkmin(ans,Special::Unique());
	chkmin(ans,Min_normal::solve());
	for(int i=1;i<=n;i++)
		a[i].x=-a[i].x;
	chkmin(ans,Min_normal::solve());
	for(int i=1;i<=n;i++)
		a[i].y=-a[i].y;
	chkmin(ans,Min_normal::solve());
	for(int i=1;i<=n;i++)
		a[i].x=-a[i].x;
	chkmin(ans,Min_normal::solve());
	chkmin(ans,Min_extra::solve());
	for(int i=1;i<=n;i++)
		a[i].y=-a[i].y;
	chkmin(ans,Min_extra::solve());
	printf("%d\n",ans<<1);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值