Arc065_E Manhattan Compass

平面上有$N$个点$(X_i\space, Y_i)$,定义$D(a,b)=|X_a-X_b|+|Y_a-Y_b|$。

如果你当前在$(p,q)$,这个无序二元组(即$(p,q)$和$(q,p)$被认为是相同的),并且存在$r$使得$D(p, q)=D(p, r)$,那么你就可以到达$(p, r)$这个无序二元组。

一开始你在$(a,b)$,这个无序二元组,求你能到达多少不同的无序二元组。

 

题解

这道题其实没有任何大的思维跨越,大体思想就是先求出$(a,b)$的曼哈顿距离,然后$bfs$看可以连出去多少条边即可。

然而这样朴素的做法是$O(N^2)$的,想办法优化这一过程。

显然枚举出发点不能进行深一步的优化,所以我们考虑优化枚举到达点的方法。

不难发现,从固定曼哈顿距离确定每一个点出发到达的点一定在一个斜$45$度的正方形上,可以对于每一条正反斜$45$度的直线开一个$vector$按$x$升序存下内部的点,这样就可以在$vector$上进行二分。

至于如何将这些点依次加入队列,可以直接开一个并查集维护$vector$中右侧最近的没有被删除的点,由于每个点只会被删除一次,所以总复杂度$O(N)$

考虑到每次在$vector$上找点还需要二分,所以常数较大,但复杂度仍是$O(N\log N)$

#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdio>
#include<cmath>
#include<map>
#define LL long long
#define M 100020
using namespace std;
int read(){
	int nm=0,fh=1; char cw=getchar();
	for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
	for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
	return nm*fh;
}
int n,m,cnt,A,B,q[M],hd,tl,D,X[M],Y[M];
LL ans;
bool vis[M];
vector<int> t[M<<1],f[M<<1];
map<int,int> ID1,ID2;
int fd(int kd,int x){return f[kd][x]==x?x:f[kd][x]=fd(kd,f[kd][x]);}
bool cmp(int x,int y){return X[x]<X[y];}
void gtin(int x){
	int tar; X[x]=read(),Y[x]=read();
	if(ID1.count(X[x]+Y[x])) tar=ID1[X[x]+Y[x]];
	else cnt++,tar=ID1[X[x]+Y[x]]=cnt,f[tar].push_back(0);
	t[tar].push_back(x),f[tar].push_back(f[tar].size());
	if(ID2.count(X[x]-Y[x])) tar=ID2[X[x]-Y[x]];
	else cnt++,tar=ID2[X[x]-Y[x]]=cnt,f[tar].push_back(0);
	t[tar].push_back(x),f[tar].push_back(f[tar].size());
}
int getpos(int kd,int minx){
	int l=0,r=t[kd].size(),fin=0,md;
	while(l<=r){
		md=((l+r)>>1);
		if(md<t[kd].size()&&X[t[kd][md]]<minx) l=md+1;
		else fin=md,r=md-1;
	} return fin;
}
void del(int x){
	vis[x]=true; q[tl++]=x;
	int kd=ID1[X[x]+Y[x]],fin; fin=getpos(kd,X[x]);
	f[kd][fin]=fd(kd,fin+1),kd=ID2[X[x]-Y[x]];
	fin=getpos(kd,X[x]),f[kd][fin]=fd(kd,fin+1);
}
void calc(int kd,int lx,int rx,bool down){
	int t1=getpos(kd,lx),t2=getpos(kd,rx),tot=t[kd].size();
	if(down&&t1<tot&&X[t[kd][t1]]<=lx) t1++;
	if(down&&t2<tot&&X[t[kd][t2]]<=rx) t2++; ans+=t2-t1;
	for(t1=fd(kd,t1);t1<t2&&t1<tot;t1=fd(kd,t1+1)) del(t[kd][t1]);
	
}
void getans(int x){
	if(ID2.count(X[x]-Y[x]-D)) calc(ID2[X[x]-Y[x]-D],X[x]-D,X[x],false);
	if(ID1.count(X[x]+Y[x]+D)) calc(ID1[X[x]+Y[x]+D],X[x],X[x]+D,false);
	if(ID1.count(X[x]+Y[x]-D)) calc(ID1[X[x]+Y[x]-D],X[x]-D,X[x],true);
	if(ID2.count(X[x]-Y[x]+D)) calc(ID2[X[x]-Y[x]+D],X[x],X[x]+D,true);
}
void solve(){
	del(A),del(B),D=abs(X[A]-X[B])+abs(Y[A]-Y[B]);
	while(hd<tl){int now=q[hd++];getans(now);}
}
int main(){
	n=read(),A=read(),B=read();
	for(int i=1;i<=n;i++) gtin(i);
	for(int i=1;i<=cnt;i++) sort(t[i].begin(),t[i].end(),cmp);
	solve(),printf("%lld\n",ans/2); return 0;
}

 

  

 

转载于:https://www.cnblogs.com/OYJason/p/9666890.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
城市应急指挥系统是智慧城市建设的重要组成部分,旨在提高城市对突发事件的预防和处置能力。系统背景源于自然灾害和事故灾难频发,如汶川地震和日本大地震等,这些事件造成了巨大的人员伤亡和财产损失。随着城市化进程的加快,应急信息化建设面临信息资源分散、管理标准不统一等问题,需要通过统筹管理和技术创新来解决。 系统的设计思路是通过先进的技术手段,如物联网、射频识别、卫星定位等,构建一个具有强大信息感知和通信能力的网络和平台。这将促进不同部门和层次之间的信息共享、交流和整合,提高城市资源的利用效率,满足城市对各种信息的获取和使用需求。在“十二五”期间,应急信息化工作将依托这些技术,实现动态监控、风险管理、预警以及统一指挥调度。 应急指挥系统的建设目标是实现快速有效的应对各种突发事件,保障人民生命财产安全,减少社会危害和经济损失。系统将包括预测预警、模拟演练、辅助决策、态势分析等功能,以及应急值守、预案管理、GIS应用等基本应用。此外,还包括支撑平台的建设,如接警中心、视频会议、统一通信等基础设施。 系统的实施将涉及到应急网络建设、应急指挥、视频监控、卫星通信等多个方面。通过高度集成的系统,建立统一的信息接收和处理平台,实现多渠道接入和融合指挥调度。此外,还包括应急指挥中心基础平台建设、固定和移动应急指挥通信系统建设,以及应急队伍建设,确保能够迅速响应并有效处置各类突发事件。 项目的意义在于,它不仅是提升灾害监测预报水平和预警能力的重要科技支撑,也是实现预防和减轻重大灾害和事故损失的关键。通过实施城市应急指挥系统,可以加强社会管理和公共服务,构建和谐社会,为打造平安城市提供坚实的基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值