【bzoj2000】[Hnoi2010]stone 取石头游戏

Description

A 公司正在举办一个智力双人游戏比赛—-取石子游戏,游戏的获胜者将会获得 A 公司提
供的丰厚奖金,因此吸引了来自全国各地的许多聪明的选手前来参加比赛。
与经典的取石子游戏相比,A公司举办的这次比赛的取石子游戏规则复杂了很多:
 总共有N堆石子依次排成一行,第i堆石子有 ai个石子。
 开始若干堆石子已被 A公司故意拿走。
 然后两个玩家轮流来取石子,每次每个玩家可以取走一堆中的所有石子,但有一个限
制条件:一个玩家若要取走一堆石子,则与这堆石子相邻的某堆石子已被取走(之前被
某个玩家取走或开始被A公司故意拿走)。注意:第 1堆石子只与第 2堆石子相邻,第
N堆石子只与第N-1堆石子相邻,其余的第 i堆石子与第i-1堆和第 i+1 堆石子相邻。
 所有石子都被取走时,游戏结束。谁最后取得的总石子数最多,谁就获得了这场游戏
的胜利。
作为这次比赛的参赛者之一,绝顶聪明的你,想知道对于任何一场比赛,如果先手者和后
手者都使用最优的策略,最后先手者和后手者分别能够取得的总石子数分别是多少。

Input

第一行是一个正整数N,表示有多少堆石子。输入文件第二行是用空格隔开的N个非负整数a1, a2, …, aN,其中ai表示第i堆石子有多少个石子,ai = 0表示第i堆石子开始被A公司故意拿走。输入的数据保证0≤ai≤100,000,000,并且至少有一个i使得ai = 0。30%的数据满足2≤N≤100,100%的数据满足2≤N≤1,000,000。

Output

仅包含一行,为两个整数,分别表示都使用最优策略时,最后先手者
和后手者各自能够取得的总石子数,并且两个整数间用一个空格隔开。

Sample Input

8

1 2 0 3 7 4 0 9

Sample Output

17 9

【样例解释】:两个玩家都使用最优策略时取走石子的顺序依次为9, 2, 1, 4, 7, 3,因此先手

者取得9 + 1 + 7 = 17个石子,后手者取得2 + 4 + 3 = 9个石子。

题解

什么鬼咯(╯‵□′)╯︵┻━┻。
可以先看CA娘的题解脑补一下:
http://blog.csdn.net/CreationAugust/article/details/48246581
我反正是说不清楚。
我们考虑求两个人的差。
首先对于一组 {ai1aiai+1ai 满足这个条件的三个数,那么必然一个人取 ai1 ai+1 另一个人取 ai 。(感性地觉得没有任何问题)这三个数就可以合并到一个 ai1+ai+1ai 里面了。
然后对于从最左端合最右端开始的递减,根据奇偶性相邻的两个数分别给两个人就好。
剩下的都是可以随便取的较大的值,按照大小直接取就好了。
(完全不会证明)

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int N = 1000000 + 10;
int n, top, tot, cnt;
ll sum, ans, stk[N], a[N];
bool used[N];

inline ll read(){
    ll x = 0, f = 1; char c = getchar();
    while(!isdigit(c)) { if(c == '-') f = -1; c = getchar(); }
    while(isdigit(c)) { x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}

bool check(int x){
    return !used[x] && !used[x-1] && !used[x+1] && stk[x-1] <= stk[x] && stk[x] >= stk[x+1];
}

void work(){
    n = (int) read();
    for(int i = 1; i <= n; i++){
        stk[++top] = read();
        sum += stk[top];
        used[top] = (stk[top] == 0);
        cnt ^= !used[top];
        while(top > 2 && check(top-1)){
            stk[top-2] += stk[top] - stk[top-1];
            top -= 2;
        }
    }
    int i, j;
    for(i = 1; !used[i] && !used[i+1] && stk[i] >= stk[i+1]; i += 2)
        ans += (stk[i] - stk[i+1]) * (cnt?1:-1);
    for(j = top; !used[j] && !used[j-1] && stk[j] >= stk[j-1]; j -= 2)
        ans += (stk[j] - stk[j-1]) * (cnt?1:-1);
    for(int k = i; k <= j; k++)
        if(!used[k]) a[++tot] = stk[k];
    sort(a+1, a+tot+1, greater<ll>());
    for(int k = 1; k <= tot; k++) ans += (k&1) ? a[k] : -a[k];
    printf("%lld %lld\n", sum+ans>>1, sum-ans>>1);
}

int main(){
    work();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值