【HNOI2010】【BZOJ2000】stone 取石头游戏

BZOJ上的Source是骗子QAQ这题根本不是SG函数QAQ
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个石子。
HINT

Source

Sg
再次重复:这题根本不是SG函数!!!QAQ
其实这题是个贪心.
可以发现对每个连续的石子区间,决策与奇偶相关的.(奇数位的和及偶数位的和)
当石子序列出现了如 ai1<ai>ai+1 的形式,显然我们可以把这部分石头合并一下变成 ai1+ai+1ai ,因为我们可以发现两个人的最优决策就是分别取 ai1+ai+1 ai .这个看起来挺对的嗯>_<.
我们可以感受一下之后就可以贪心了>_<(什么鬼!)
好吧其实是这样的:我们可以发现整个数列被我们化成了两人取数之差的形式.最后把差值记录一下,再用初始数列每项的和计算一下就能得到答案.
需要注意:过程中尽量开LongLong因为好像有不少地方会爆int?
以及合并之后别忘了删除掉多余的元素!

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 1000100
#define LL long long
using namespace std;
LL a[MAXN],s[MAXN];
int prev[MAXN],next[MAXN];
LL n,num;
int b[MAXN];
int head,tail,top;
LL sum,delta;
inline void in(LL &x)
{
    char ch=getchar();x=0;
    while (!(ch>='0'&&ch<='9')) ch=getchar();
    while (ch>='0'&&ch<='9')    x=x*10+ch-'0',ch=getchar();
}
inline void del(int x)
{
    b[x]=1;
    prev[next[x]]=prev[x];
    next[prev[x]]=next[x];
}
inline bool comp(LL a,LL b)
{
    return a>b;
}
int main()
{
    in(n);
    for (int i=1;i<=n;i++)
    {
        in(a[i]);b[i]=!a[i];sum+=a[i];
        next[i]=i+1;prev[i]=i-1;num+=!b[i];
    }
    next[0]=1;prev[n+1]=n;head=1;tail=n;
    bool flag=1;
    while (flag&&head<tail)
    {
        flag=0;
        if (!b[head]&&!b[next[head]]&&a[head]>=a[next[head]])
        {
            delta+=(num&1)?a[head]-a[next[head]]:a[next[head]]-a[head];
            int tmp=next[next[head]];
            del(next[head]);del(head);head=tmp;flag=1;
        }
        if (!b[tail]&&!b[prev[tail]]&&a[tail]>=a[prev[tail]])
        {
            delta+=(num&1)?a[tail]-a[prev[tail]]:a[prev[tail]]-a[tail];
            int tmp=prev[prev[tail]];
            del(prev[tail]);del(tail);tail=tmp;flag=1;
        }
        for (int i=head;i<=tail;i++)
            if (!b[i]&&prev[i]>0&&next[i]<=n&&!b[prev[i]]&&!b[next[i]]&&a[i]>=a[prev[i]]&&a[i]>=a[next[i]])
            {
                flag=1;a[i]=a[prev[i]]+a[next[i]]-a[i];
                if (prev[i]==head)  head=i;
                if (next[i]==tail)  tail=i;
                del(prev[i]);del(next[i]);
            }
    }
    for (int i=1;i<=n;i++)
        if (!b[i])  s[++top]=a[i];
    sort(s+1,s+top+1,comp);
    for (int i=1;i<=top;i++)    delta+=(i&1)?s[i]:-s[i];
    cout<<(sum+delta)/2<<' '<<(sum-delta)/2<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值