码迷,mamicode.com
首页 > 其他好文 > 详细

[ARC120E] 1D Party

时间:2021-06-02 15:24:29      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:for   put   stdout   lse   tar   个人   两种   read   targe   

前言

\(\tt dp\) 就不要乱猜结论贪心。

题目

AtCoder

题目大意:

给出 \(n\) 个人的数轴上的坐标 \(a_i\)保证为偶数,每个人的速度至多为 \(1\),需要使得每一个 \(i\in[1,n-1]\),第 \(i\) 个人与第 \(i+1\) 个人相遇过,求最小时间。

\(1\le n\le 2\times 10^5;0\le a_i\le 10^9;a_1<a_2<...<a_n.\)

讲解

本做法时间复杂度为 \(O(n\log_2n)\)

首先二分一个时间 \(t\),考虑 \(\tt dp\) 验证。

我们发现对于每个人,无非只有两种策略:

  • 先向右走,碰到第 \(i+1\) 个人后向左走。
  • 先向左走,碰到第 \(i-1\) 个人后向右走。

\(dp_{i,0}\) 表示先向右走,向右最多能走多远;\(dp_{i,1}\) 表示先向左走,向右最多能走多远。

注意这里定义的都是向右能走多远。特别的,\(dp_{1,0}=dp_{1,1}=t\)

然后开始大力分类讨论。为了表述方便,我们将第 \(i-1\) 个人称为,第 \(i\) 个人称为

1.她先向右走,他先向左走。

合法条件:由于她先向右走,所以在她走到右边尽头的时候他必须已经到达,否则无法碰面。

if(a[i-1]+dp[i-1][0] >= a[i]-dp[i-1][0])

显然一旦相遇,他就会往右走,所以有如下转移:

up(dp[i][1],t-(a[i]-a[i-1]));

ps: \(up(x,y)\) 表示 \(x=\max(x,y)\)

2.她先向右走,他先向右走。

合法条件同上(可以视作他先向右走0步)。

此时他们的间距保持不变,而一旦她向左走,他就追不上她了,所以有:

up(dp[i][0],dp[i-1][0]-(a[i]-a[i-1])/2);

3.她先向左走,他先向右走。

合法条件:她在 \(t\) 秒末一定会在 \(a_{i-1}+dp_{i,1}\),所以此时他一定要能到此处才行。

if(a[i-1]+dp[i-1][1] >= a[i]-t)

他向右走可以支配的时间显然为总时间减去他到她右端点的时间,而向右走显然是个来回,所以还要除以二。

up(dp[i][0],(t-a[i]+a[i-1]+dp[i-1][1])/2);

4.她先向左走,他先向左走。

合法条件同上。

相遇后显然两人都应该向右走,所以有:

up(dp[i][1],dp[i-1][1]+a[i-1]-a[i]);

代码

int dp[MAXN][2];//0:go right first; 1:opposite
void up(int &x,int y){x = Max(x,y);}
bool check(int t)
{
	for(int i = 2;i <= n;++ i) dp[i][0] = dp[i][1] = -INF;
	dp[1][0] = dp[1][1] = t;
	for(int i = 2;i <= n;++ i)
	{
		bool suc = 0;
		//rl & rr
		if(dp[i-1][0] >= (a[i]-a[i-1])/2)
		{
			up(dp[i][1],t-(a[i]-a[i-1]));
			up(dp[i][0],dp[i-1][0]-(a[i]-a[i-1])/2);
			suc = 1;
		}
		//lr && ll
		if(a[i-1]+dp[i-1][1] >= a[i]-t)
		{
			up(dp[i][0],(t-a[i]+a[i-1]+dp[i-1][1])/2);
			up(dp[i][1],dp[i-1][1]+a[i-1]-a[i]);
			suc = 1;
		}
		if(!suc) return 0;
	}
	return 1;
}

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	n = Read();
	for(int i = 1;i <= n;++ i) a[i] = Read();
	if(n <= 3) {Put((a[n]-a[1]) / 2);return 0;}
	int ans = (a[n]-a[1]) / 2,l = 0,r = (a[n]-a[1])/2;
	while(l <= r)
	{
		int mid = (l+r) >> 1;
		if(check(mid)) r = mid-1,ans = mid;
		else l = mid+1;
	}
	Put(ans);
	return 0;
}

后记

第一次尝试用花里胡哨的方式写题解(指使用他和她),如有不清楚之处,还请见谅,可以在评论区留言。

[ARC120E] 1D Party

标签:for   put   stdout   lse   tar   个人   两种   read   targe   

原文地址:https://www.cnblogs.com/PPLPPL/p/14824572.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!