BZOJ 2000([Hnoi2010]stone 取石头游戏-贪心博弈)

有2个栈和一些双端队列,每次可以从栈首或双端队列边取走一个数,取走数的和多大的人赢。
如果先手者和后手者都使用最优的策略,最后先手者和后手者分别能够取得的总石子数分别是多少。

对于一个栈,取法唯一,
对双端队列,长度为奇数取法唯一,否则先手可以取走max(所有奇数位,所有偶数位)

1.
1 2 0 4 3 3 4 0 2 1
如果所有序列都是单调(且取的那一边最大)的,取最大那个就行了

现考虑2个贪心
对于栈 a1,a2,a3,...an
1.如果栈底2个元素 a1>a2 ,那么取 a2 不优,因为对手可以马上取 a1 ,
那么把它放到最后,根据奇偶性取

2.对于双端队列 ..ai1,ai,ai+1..
如果 ai>ai1,ai>ai+1
那么先取 ai 2侧肯定不优,如果你被迫拿了,对手可以迅速拿走 ai ,剩下的只能是你拿
那么用贡献 δ=ai1+ai+1ai 替换

若干次后能保证序列单调,贪心取最大即可

#include<bits/stdc++.h>
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define Rep(i,n) for(int i=0;i<n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define ForkD(i,k,n) for(int i=n;i>=k;i--)
#define RepD(i,n) for(int i=n;i>=0;i--)
#define Forp(x) for(int p=Pre[x];p;p=Next[p])
#define Forpiter(x) for(int &p=iter[x];p;p=Next[p])  
#define Lson (o<<1)
#define Rson ((o<<1)+1)
#define MEM(a) memset(a,0,sizeof(a));
#define MEMI(a) memset(a,127,sizeof(a));
#define MEMi(a) memset(a,128,sizeof(a));
#define INF (2139062143)
#define F (100000007)
#define pb push_back
#define mp make_pair 
#define fi first
#define se second
#define MAXN (1000000+10)
typedef long long ll;
typedef unsigned long long ull;
ll mul(ll a,ll b){return (a*b)%F;}
ll add(ll a,ll b){return (a+b)%F;}
ll sub(ll a,ll b){return (a-b+llabs(a-b)/F*F+F)%F;}
void upd(ll &a,ll b){a=(a%F+b%F)%F;}
int n;
ll s1=0,s2=0,a[MAXN];


ll s=0,cnt=0;
bool b[MAXN];
class List{
public:
    int pre[MAXN],next[MAXN];
    int Head,Tail;
    List(){
        Head=Tail=0;
        MEM(pre) MEM(next) 
    }
    void mem(int n) {
        Head=1,Tail=n;
        For(i,n) pre[i]=i-1,next[i]=i+1;
        MEM(b)
    }
    void del(int p){
        if (p==Head) Head=next[p];
        if (p==Tail) Tail=pre[p];
        pre[next[p]]=pre[p];
        next[pre[p]]=next[p];
    }

    bool work1() {
        bool flag=0;

        while( b[Head] && b[next[Head]] && a[Head]>=a[ next[Head] ] ) {
            if (cnt&1) s1+=a[Head], s2+=a[next[Head]];
            else s1+=a[next[Head]], s2+=a[Head];
            del(Head); del(Head); 
            flag=1;
        }

        while( b[Tail] && b[pre[Tail]] && a[Tail]>=a[ pre[Tail] ] ) {
            if (cnt&1) s1+=a[Tail], s2+=a[pre[Tail]];
            else s1+=a[pre[Tail]], s2+=a[Tail];
            del(Tail); del(Tail); 
            flag=1;
        }
        return flag;
    }
    bool work2() {
        bool flag=0;
        for(int i=next[Head]; next[i] ; i=next[i] ) {
            if (b[ pre[ i ] ] && b[i] && b[next[i]] && a[pre[i]] <a[i] && a[i]> a[next[i]] )  
            { 
                a[i] = a[ pre [ i ] ] + a[ next[i]] - a[i];
                del(pre[i]); del(next[i]); 
                flag=1;
            }
        } 
        return flag;
    } 
    ll ans[MAXN];
    int siz;
    void work() {
        while (work1() || work2() );
        siz=0;
        for(int i=Head;i<=Tail;i=next[i]) if (b[i]) ans[++siz] = a[i] ;

        sort(ans+1,ans+1+siz);

        ll s3=s-s1-s2,p=0; 

        bool flag=1;
        ForD(i,siz) {
            if (flag) p+=ans[i];else p-=ans[i];
            flag^=1;
        }


        cout<<s1 + (s3+p)/2<<' '<<s2+(s3-p)/2<<endl;



    }   

}S;

int main()
{
//  freopen("bzoj2000.in","r",stdin);
//  freopen(".out","w",stdout);

    cin>>n;
    S.mem(n);
    For(i,n) scanf("%lld",&a[i]),s+=a[i], b[i]=(bool)a[i] ,cnt+=(bool)a[i];



    S.work();



    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值