• [ARC120E] 1D Party


    前言

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

    题目

    AtCoder

    题目大意:

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

    (1le nle 2 imes 10^5;0le a_ile 10^9;a_1<a_2<...<a_n.)

    讲解

    本做法时间复杂度为 (O(nlog_2n))

    首先二分一个时间 (t),考虑 ( t 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;
    }
    

    后记

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

  • 相关阅读:
    【洛谷3527】[POI2011] MET-Meteors(树状数组+整体二分)
    【洛谷1580】yyy loves Easter_Egg I(字符串处理题)
    【BZOJ4866】[YNOI2017] 由乃的商场之旅(莫队)
    【BZOJ4810】[YNOI2017] 由乃的玉米田(莫队+bitset)
    【洛谷1494】[国家集训队] 小Z的袜子(莫队)
    【BZOJ3668】[NOI2014] 起床困难综合症(位运算思想)
    【BZOJ3720】Gty的妹子树(主席树+时间分块)
    【BZOJ2427】[HAOI2010] 软件安装(缩点+树形DP)
    【洛谷3648】[APIO2014] 序列分割(斜率优化DP)
    动态规划专题(五)——斜率优化DP
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/14824572.html
Copyright © 2020-2023  润新知