题解:洛谷 P10801([CEOI 2024] 海战)

1. Description

捷克海军司令官翁德拉刚刚晋升为大元帅,正享受着新职位的安稳,却突然被政府通知海军将被裁撤。
翁德拉决心证明捷克海军的重要性。他通过间谍得知,一场四国海军巨舰对决即将展开。如果能赢得这场战役,无疑能向政府有力地展示海军价值。
然而,捷克海军既无战舰也无港口。但翁德拉想到,如果他的间谍能夺取几艘参战舰艇,或许还有一线生机。关键是,如何预知哪些船能在这场海战中幸存下来呢?
海战规则如下:

  • 战前,第 \(i\) 艘战舰位于坐标 \((x_i, y_i)\) 处,其中 \(x_i\)\(y_i\) 均为偶数。每艘战舰隶属于北方、南方、东方或西方舰队之一。
  • 海战分回合进行。每回合:
    • 每艘战舰同时向其所属舰队方向移动一格。
    • 如果两艘或以上战舰占据同一格,它们将相撞沉没,从海图上消失。
  • 当不再发生碰撞时,海战结束。存活的战舰是指海战结束后仍留在海图上的战舰。
    各舰队战舰的移动方向及坐标变化如下:
  • 北方舰队:\(y\) 坐标减 \(1\)
  • 南方舰队:\(y\) 坐标加 \(1\)
  • 东方舰队:\(x\) 坐标加 \(1\)
  • 西方舰队:\(x\) 坐标减 \(1\)

2. Solution

首先,暴力做法不难想到,暴力枚举所有战舰对,计算出它们相撞的时间,然后扔进一个小根堆中依次处理即可,时间复杂度 \(O(n^2)\)
我们考虑优化这个做法,去除掉一定不会用到的战舰对。
考虑一艘向南的战舰 \(i\),一艘向东的战舰 \(j\),如果它们会在时间 \(t\) 相撞,那么满足 \(x_i+t=x_j,y_i+t=y_j\),简单整理就有 \(x_i-y_i=x_j-y_j\)
就是说,这两艘战舰想要相撞,必然就有横纵坐标之差相等,所以我们按照横纵坐标差将战舰分类。但是这样并没有实际作用,我们仍然需要进一步考虑。
如果可以相撞,一定有 \(t=x_i-x_j\),我们发现,如果将这一类的战舰按照 \(x\) 排序,一定有相邻的战舰对优于不相邻的战舰对,那么就可以将有用的战舰对优化到 \(O(n)\) 了。
其余 \(5\) 种组合,读者可自行推导,这里给出结论:
向北和向东:\(x-y\) 相等,按照 \(x\) 坐标排序。
向北和向西:\(x+y\) 相等,按照 \(x\) 坐标排序。
向北和向南:\(x\) 相等,按照 \(y\) 坐标排序。
向东和向西:\(y\) 相等,按照 \(x\) 坐标排序。
向东和向南:\(x+y\) 相等,按照 \(x\) 坐标排序。
向西和向南:\(x-y\) 相等,按照 \(x\) 坐标排序。
可以使用 set 维护同一类的战舰。
在小根堆处理的时候,我们每一次取出堆顶的战舰对,判断是否相撞,然后在对应的 set 中删除这两艘战舰,最后加入新的可能的战舰对即可,不难判断这样的时间复杂度是 \(O(n\log n)\) 的。

3. Code

/*by ChenMuJiu*/
/*略去缺省源与快读快写*/
const int N=2e5+5,dx[]={0,1,-1,0},dy[]={-1,0,0,1},inf=0x3f3f3f3f;
int n;
int x[N],y[N],d[N],t[N];
struct Node{
	int first,second,idx;
	bool operator <(const Node &T)const{
		if(first^T.first)return first<T.first;
		return second<T.second;
	}
};
set<Node>idx[4][4];
struct event{
	int t,x,y;
	bool operator >(const event &T)const{
		return t>T.t;
	}
};
priority_queue<event,vector<event>,greater<event>>q;
int cal(int i,int j){
	if(d[i]==d[j])return -1;
	int t=-1;
	if(dx[d[i]]!=dx[d[j]])t=(x[i]-x[j])/(dx[d[j]]-dx[d[i]]);
	if(dy[d[i]]!=dy[d[j]])t=(y[i]-y[j])/(dy[d[j]]-dy[d[i]]);
	if(t<0)return -1;
	if(x[i]+t*dx[d[i]]!=x[j]+t*dx[d[j]])return -1;
	if(y[i]+t*dy[d[i]]!=y[j]+t*dy[d[j]])return -1;
	return t;
}
void delet(int id){
	set<Node>::iterator it;
	if(d[id]==0){
		it=idx[0][1].lower_bound({x[id]-y[id],x[id],id});
		if(it!=idx[0][1].begin()&&next(it)!=idx[0][1].end()){
			int t=cal(prev(it)->idx,next(it)->idx);
			if(~t)
				q.push({t,prev(it)->idx,next(it)->idx});
		}
		idx[0][1].erase(it);
		
		it=idx[0][2].lower_bound({x[id]+y[id],x[id],id});
		if(it!=idx[0][2].begin()&&next(it)!=idx[0][2].end()){
			int t=cal(prev(it)->idx,next(it)->idx);
			if(~t)
				q.push({t,prev(it)->idx,next(it)->idx});
		}
		idx[0][2].erase(it);
		
		it=idx[0][3].lower_bound({x[id],y[id],id});
		if(it!=idx[0][3].begin()&&next(it)!=idx[0][3].end()){
			int t=cal(prev(it)->idx,next(it)->idx);
			if(~t)
				q.push({t,prev(it)->idx,next(it)->idx});
		}
		idx[0][3].erase(it);
	}else if(d[id]==1){
		it=idx[0][1].lower_bound({x[id]-y[id],x[id],id});
		if(it!=idx[0][1].begin()&&next(it)!=idx[0][1].end()){
			int t=cal(prev(it)->idx,next(it)->idx);
			if(~t)
				q.push({t,prev(it)->idx,next(it)->idx});
		}
		idx[0][1].erase(it);
		
		it=idx[1][2].lower_bound({y[id],x[id],id});
		if(it!=idx[1][2].begin()&&next(it)!=idx[1][2].end()){
			int t=cal(prev(it)->idx,next(it)->idx);
			if(~t)
				q.push({t,prev(it)->idx,next(it)->idx});
		}
		idx[1][2].erase(it);
		
		it=idx[1][3].lower_bound({x[id]+y[id],x[id],id});
		if(it!=idx[1][3].begin()&&next(it)!=idx[1][3].end()){
			int t=cal(prev(it)->idx,next(it)->idx);
			if(~t)
				q.push({t,prev(it)->idx,next(it)->idx});
		}
		idx[1][3].erase(it);
	}else if(d[id]==2){
		it=idx[0][2].lower_bound({x[id]+y[id],x[id],id});
		if(it!=idx[0][2].begin()&&next(it)!=idx[0][2].end()){
			int t=cal(prev(it)->idx,next(it)->idx);
			if(~t)
				q.push({t,prev(it)->idx,next(it)->idx});
		}
		idx[0][2].erase(it);
		
		it=idx[1][2].lower_bound({y[id],x[id],id});
		if(it!=idx[1][2].begin()&&next(it)!=idx[1][2].end()){
			int t=cal(prev(it)->idx,next(it)->idx);
			if(~t)
				q.push({t,prev(it)->idx,next(it)->idx});
		}
		idx[1][2].erase(it);
		
		it=idx[2][3].lower_bound({x[id]-y[id],x[id],id});
		if(it!=idx[2][3].begin()&&next(it)!=idx[2][3].end()){
			int t=cal(prev(it)->idx,next(it)->idx);
			if(~t)
				q.push({t,prev(it)->idx,next(it)->idx});
		}
		idx[2][3].erase(it);
	}else{
		it=idx[0][3].lower_bound({x[id],y[id],id});
		if(it!=idx[0][3].begin()&&next(it)!=idx[0][3].end()){
			int t=cal(prev(it)->idx,next(it)->idx);
			if(~t)
				q.push({t,prev(it)->idx,next(it)->idx});
		}
		idx[0][3].erase(it);
		
		it=idx[1][3].lower_bound({x[id]+y[id],x[id],id});
		if(it!=idx[1][3].begin()&&next(it)!=idx[1][3].end()){
			int t=cal(prev(it)->idx,next(it)->idx);
			if(~t)
				q.push({t,prev(it)->idx,next(it)->idx});
		}
		idx[1][3].erase(it);
		
		it=idx[2][3].lower_bound({x[id]-y[id],x[id],id});
		if(it!=idx[2][3].begin()&&next(it)!=idx[2][3].end()){
			int t=cal(prev(it)->idx,next(it)->idx);
			if(~t)
				q.push({t,prev(it)->idx,next(it)->idx});
		}
		idx[2][3].erase(it);
	}
}
signed main(){
	read(n);
	for(int i=1;i<=n;i++){
		read(x[i]),read(y[i]);
		char c=getchar();
		while(c!='N'&&c!='E'&&c!='W'&&c!='S')c=getchar();
		if(c=='N')d[i]=0;
		else if(c=='E')d[i]=1;
		else if(c=='W')d[i]=2;
		else d[i]=3;
		if(d[i]==0){
			idx[0][1].insert({x[i]-y[i],x[i],i});
			idx[0][2].insert({x[i]+y[i],x[i],i});
			idx[0][3].insert({x[i],y[i],i});
		}else if(d[i]==1){
			idx[0][1].insert({x[i]-y[i],x[i],i});
			idx[1][2].insert({y[i],x[i],i});
			idx[1][3].insert({x[i]+y[i],x[i],i});
		}else if(d[i]==2){
			idx[0][2].insert({x[i]+y[i],x[i],i});
			idx[1][2].insert({y[i],x[i],i});
			idx[2][3].insert({x[i]-y[i],x[i],i});
		}else{
			idx[0][3].insert({x[i],y[i],i});
			idx[1][3].insert({x[i]+y[i],x[i],i});
			idx[2][3].insert({x[i]-y[i],x[i],i});
		}
	}
	for(int i=0;i<4;i++){
		for(int j=i+1;j<4;j++){
			auto it=idx[i][j].begin();
			while(it!=idx[i][j].end()){
				if(next(it)!=idx[i][j].end()){
					int t=cal(next(it)->idx,it->idx);
					if(~t)q.push({t,next(it)->idx,it->idx});
				}
				it++;
			}
		}
	}
	memset(t,0x3f,sizeof(t));
	while(!q.empty()){
		auto tmp=q.top();
		q.pop();
		if(t[tmp.x]!=inf&&t[tmp.x]!=tmp.t)continue;
		if(t[tmp.y]!=inf&&t[tmp.y]!=tmp.t)continue;
		if(t[tmp.x]==inf){
			t[tmp.x]=tmp.t;
			delet(tmp.x);
		}
		if(t[tmp.y]==inf){
			t[tmp.y]=tmp.t;
			delet(tmp.y);
		}
	}
	for(int i=1;i<=n;i++)
		if(t[i]==inf)write(i),Nxt;
}
posted @ 2026-02-01 21:31  陈牧九  阅读(3)  评论(0)    收藏  举报