CF1525C-Robot Collisions

C - Robot Collisions

题目描述

有n个机器人沿着x轴线行驶。还有两个墙:一个在坐标0处,另一个在坐标m处。

第i个机器人以整数坐标 x i x_i xi 0 < x i < m 0<x_i<m 0<xi<m )开始,以1单位/秒的速度向左(向右)移动。没有两个机器人从同一个坐标开始。

当一个机器人到达一堵墙时,它会立即转身,以同样的速度继续朝相反的方向行驶。

每当几个机器人在同一个整数坐标处相遇时,它们就会碰撞并爆炸成尘埃。一旦一个机器人爆炸了,它就不会和其他机器人相撞。请注意,如果多个机器人在非整数坐标处相遇,则什么也不会发生。

对于每个机器人,找出是否发生过爆炸,如果发生了爆炸,输出爆炸的时间,否则输出-1。

数据范围与提示

2 ≤ n ≤ 300000 2\le n\le 300000 2n300000

思路

首先由于机器人只有恰好走在整数坐标时相撞才爆炸,所以坐标奇偶性不同的机器人一定不相撞。容易发现,机器人的坐标沿着墙对称过去后的奇偶性不变,也就是说,两个坐标奇偶性相同的机器人,其中一个遇墙反弹后,它们奇偶性仍相同。所以我们可以把奇偶坐标分开来讨论。

另外,奇偶性相同的机器人一定不会互相越过,相遇就爆炸。而且由于初始坐标不相同,爆炸一定是两个机器人相撞,不会是三个及以上。然后问题就转换成机器人两两配对。

另外我们还发现,无论什么时候,面向墙的离墙最近的机器人的行进都可以等效于坐标沿墙对称、方向反向,而两个最近的方向相对的机器人一定会先爆炸、消失,然后给其它机器人让路。

所以这就是一个简单的括号匹配问题。

我们只需要每次把靠左边墙的右括号对称反向后再与后面的配对,最后倒着处理掉剩下的左括号即可。复杂度只有排序的 O ( n log ⁡ n ) O(n\log n) O(nlogn) ,也可以用基排优化到 O ( n ) O(n) O(n)

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#define ll long long
#define MAXN 300005
#define uns unsigned
#define INF 0x3f3f3f3f
using namespace std;
inline ll read(){
	ll x=0;bool f=1;char s=getchar();
	while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
	return f?x:-x;
}
int n,a[MAXN];
ll x[MAXN],m,e[MAXN];
bool t[MAXN];
stack<int>st;
inline bool cmp(int c,int d){return x[c]<x[d];}
int main()
{
	for(int T=read();T--;){
		n=read(),m=read();
		for(int i=1;i<=n;i++)x[i]=read(),a[i]=i;
		for(int i=1;i<=n;i++){
			char s=getchar();
			while(s!='L'&&s!='R')s=getchar();
			t[i]=(s=='L');
		}
		sort(a+1,a+1+n,cmp);
		for(int i=1;i<=n;i++)e[i]=0;
		while(!st.empty())st.pop();
		for(int K=1;K<=n;K++){
			int i=a[K];
			if(x[i]&1){
				if(t[i]&&st.empty())t[i]=0,x[i]=-x[i];
				if(t[i]){
					int u=st.top();st.pop();
					e[u]=e[i]=(x[i]-x[u])>>1;
				}
				else st.push(i);
			}
		}
		while(st.size()>1){
			int v=st.top();st.pop();
			int u=st.top();st.pop();
			x[v]=(m<<1)-x[v];
			e[u]=e[v]=(x[v]-x[u])>>1;
		}
		
		while(!st.empty())st.pop();
		for(int K=1;K<=n;K++){
			int i=a[K];
			if((~x[i]&1)&&x[i]>0){
				if(t[i]&&st.empty())t[i]=0,x[i]=-x[i];
				if(t[i]){
					int u=st.top();st.pop();
					e[u]=e[i]=(x[i]-x[u])>>1;
				}
				else st.push(i);
			}
		}
		while(st.size()>1){
			int v=st.top();st.pop();
			int u=st.top();st.pop();
			x[v]=(m<<1)-x[v];
			e[u]=e[v]=(x[v]-x[u])>>1;
		}
		for(int i=1;i<=n;i++)
			printf("%lld ",e[i]>0?e[i]:-1);
		putchar('\n');
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值