洛谷 P3407 散步 (二分答案+判定 或 模拟)

散步

题目描述

一条道路上,位置点用整数A表示。

当A=0时,有一个王宫。当A>0,就是离王宫的东边有A米,当A<0,就是离王宫的西边有A米。

道路上,有N个住宅从西向东用1-N来标号。每个住宅有一个人。住宅只会存在于偶数整数点。

该国国王认为,国民体质下降,必须要多运动,于是下命令所有人都必须出门散步。所有的国民,一秒钟可以走1米。每个国民各自向东或者向西走。这些方向你是知道的。命令发出后所有人同时离开家门开始散步。

然而该国的国民个都很健谈,如果在散步途中两个人相遇,就会停下来交谈。正在走路的人碰到已经停下来的人(重合)也会停下来交谈。一但停下来,就会聊到天昏地暗,忘记了散步。

现在命令已经发出了T秒,该国有Q个重要人物,国王希望能够把握他们的位置。你能帮他解答吗?

输入输出格式

输入格式:

第一行是3个整数,N,T,Q

接下来N行,每行两个整数Ai,Ri。Ai是家的坐标,如果Ri是1,那么会向东走,如果是2,向西。数据保证Ai是升序排序,而且不会有两个人初始位置重合。

接下来Q行,每行一个整数,表示国王关心的重要人物。

输出格式:

Q行,每行一个整数,表示这个人的坐标。

输入输出样例

输入样例#1:
6 6 4
-10 1
-6 2
-4 1
2 1
6 2
18 2
2
3
4
6
输出样例#1:
-8
2
4
12










说明

20%数据 N<=100,T<=10000

另外20%数据 N<=5000

另外20%数据 从最西边数起连续的若干国民全部往东,剩下的全部往西

100%数据 Ai为偶数,|Ai|<=10^18,|T|<=10^18,1<=Q<=N<=100000.


题解:二分答案+判定 或 模拟

先说一下自己比较愚蠢的二分做法。我们发现如果一个人会停下那么一定是与他相向而行的最近的人相遇,然后停下。于是我们可以将两方向的人分开,然后在反方向的人中二分找到这个人。

对于每个询问我们二分相遇的时间,然后判定。

但是有一个问题,就是两个人的关系不是一一对应的,就是x最近的为y,但y最近的不一定是x。

于是我们需要判定与当前位置最近的点最终会停在哪里,保证我们走过二分的时间当前点不会超过那个位置。

其实不用这么麻烦,我们用栈将所有的向东走的存下来,然后遇到一个向西走的,那此时栈顶的元素一定是离他最近的,算出两个人停下的位置,将栈弹空,保证走T秒的位置不超过这个位置即可。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#define N 100003
#define LL long long
using namespace std;
LL n,m,t,q,x[N],y[N],pos[N],cnt1,cnt;
struct data
{
	int pd;
	LL pos;
}a[N];
int find(int pd,LL x1)
{
	if (pd==1){
		int l=1; int r=cnt1; int ans=cnt1+1;
		while (l<=r)
		{
			int mid=(l+r)/2;
			if(a[y[mid]].pos>=x1)  ans=min(ans,mid),r=mid-1;
			else l=mid+1;
		} 
	    return y[ans];
	}
	int l=1; int r=cnt; int ans=0;
	while (l<=r)
	{
		int mid=(l+r)/2;
		if (a[x[mid]].pos<=x1) ans=max(ans,mid),l=mid+1;
		else r=mid-1;
	}
	return x[ans];
}
LL east(int x)
{
	LL l=1; LL r=m; LL ans=m;
	while (l<=r){
				LL mid=(l+r)/2;
				if (a[x].pos+mid>=a[pos[x]].pos-mid) ans=min(ans,mid),r=mid-1;
				else l=mid+1;
			}
	return ans;
}
LL west(int x)
{
	LL l=1; LL r=m; LL ans=m;
	while (l<=r){
	    		LL mid=(l+r)/2;
	    		if (a[x].pos-mid<=a[pos[x]].pos+mid) ans=min(ans,mid),r=mid-1;
	    		else l=mid+1;
			}
	return ans;
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("my.out","w",stdout);
	scanf("%I64d%I64d%I64d",&n,&m,&q);
	for (int i=1;i<=n;i++) scanf("%I64d%d",&a[i].pos,&a[i].pd);
	for (int i=1;i<=n;i++) if (a[i].pd==1) x[++cnt]=i;
	else y[++cnt1]=i;
	for (int i=1;i<=n;i++) pos[i]=find(a[i].pd,a[i].pos);
	//for (int i=1;i<=n;i++)
	 //cout<<pos[i]<<" ";
	//cout<<endl;
	for (int i=1;i<=q;i++){
		int x; scanf("%d",&x);
		if (pos[x]==0) {
			printf("%I64d\n",a[x].pos+(a[x].pd==1?m:-m));
			continue;
		}
		LL ans=m;
		if (a[x].pd==1){
			ans=east(x);
			if (pos[pos[x]]!=x){
				LL ans1=west(pos[x]);
				LL t=a[pos[x]].pos-ans1;
				printf("%I64d\n",min(a[x].pos+m,t));
				continue;
			}
			printf("%I64d\n",a[x].pos+ans);
	    }
	    else {
	    	ans=west(x);
	    	if (pos[pos[x]]!=x){
	    		LL ans1=east(pos[x]);
	    		LL t=a[pos[x]].pos+ans1;
	    		printf("%I64d\n",max(t,a[x].pos-m));
	    		continue;
			}
			printf("%I64d\n",a[x].pos-ans);
		}
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值