[AGC023D] Go Home

题目

点这里看题目。

分析

一眼望过去似乎就是每个人往自己的家的方向投?

幸好出题人够良心,光是样例 1 就帮你把这个错误的想法叉掉了

每个人往自己家投,前提条件是自己的票可以让自己尽早回家

比如,(n) 楼居民如果盲目向右跟风,那么很有可能投着投着,还没回到家,就因为投右的人都走得差不多了,结果反被拉着向左走,回家的时间反而变晚了。

又考虑到最后到家的人要么是第 1 幢楼的人,要么是第 (n) 幢楼的人。如果有 (p_1ge p_n),那么在某次投票中,(n) 楼居民一定会被迫跟着 1 楼居民走;而显然 1 楼居民不到家,他们就到不了家,因此他们一定会全力支持 1 楼居民提早回家。考虑到 (n) 楼居民的策略现在一定和 1 楼居民的策略基本一致,我们可以看作是将 (n) 楼居民搬迁到了 1 楼,最后再搬回 (n) 楼。

相似的,当 (p_1< p_n) 的时候,一定是 1 楼居民全力支持 (n) 楼居民,而 (n) 楼居民可能最后可以夺取掌控权,暂时还无法论断。

那么,进行“搬迁”操作后我们得到了相似的子问题,可以递归解决,时间为 (O(n))

得出结论,人多才是硬道理

小结:

  1. 分析多个对象的问题,可以抓住关键的几个对象来分析他们的策略;
  2. 相似问题、递归思想;
  3. 其中“将 (n) 楼居民搬迁到 1 楼”,这个想法很有趣;

代码

#include <set>
#include <cstdio>
using namespace std;

#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )

typedef long long LL;

const int MAXN = 1e5 + 5;

template<typename _T>
void read( _T &x )
{
    x = 0; char s = getchar(); int f = 1;
    while( s < '0' || '9' < s ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
    while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
    x *= f;
}

template<typename _T>
void write( _T x )
{
    if( x < 0 ) putchar( '-' ), x = -x;
    if( 9 < x ) write( x / 10 );
    putchar( x % 10 + '0' );
}

template<typename _T>
_T ABS( const _T a )
{
	return a < 0 ? -a : a;
}

LL X[MAXN], P[MAXN];
int N, S; LL ans = 0;

int Solve( const int l, const int r )
{
	if( S <= X[l] ) return ans += X[r] - S, X[r];
	if( S >= X[r] ) return ans += S - X[l], X[l];
	if( P[l] >= P[r] )
	{
		P[l] += P[r];
		int t = Solve( l, r - 1 );
		ans += X[r] - t;
		return X[r];
	}
	else
	{
		P[r] += P[l];
		int t = Solve( l + 1, r );
		ans += t - X[l];
		return X[l];
	}
}

int main()
{
	read( N ), read( S );
	rep( i, 1, N ) 
		read( X[i] ), read( P[i] );
	Solve( 1, N );
	write( ans ), putchar( '
' );
	return 0;
}
原文地址:https://www.cnblogs.com/crashed/p/14952235.html