【BZOJ2000】[HNOI2000]取石頭遊戲(貪心,博弈論)
阿新 • • 發佈:2018-10-25
lse long long == 並且 分開 top ans 題解 ++i
【BZOJ2000】[HNOI2000]取石頭遊戲(貪心,博弈論)
題面
BZOJ
洛谷
題解
這題好神仙啊,窩不會QaQ。
假裝一下只有三個元素\(a_{i-1},a_i,a_{i+1}\),並且滿足,\(a_{i-1}\le a_i\ge a_{i+1}\)那麽肯定是\(a_{i-1}+a_{i+1}\)、\(a_i\)這樣子分配的。那麽兩個人的差就是\(a_{i-1}+a_{i+1}-a_i\),那麽我們把\(i\)和旁邊兩個元素直接合並就好了,反正只要知道了兩個人的差和所有元素之和就能還原答案。
不難發現這樣子合並完之後序列要麽單增要麽單減。
我們發現中間被分開的一段段是一個雙端隊列,可以從兩端取。兩側被分割的部分是一個棧,只能一側取。顯然兩側的按照奇偶可以直接分配好誰去哪一側。而剩下的部分因為單調,所以顯然排序之後兩個人一個個輪流取就好了。
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; #define ll long long #define MAX 1000100 inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } int n,l,r,fr,top;ll S[MAX],sum,ans;bool vis[MAX]; bool check(int p){if(vis[p]||vis[p-1]||vis[p+1])return false;return S[p-1]<=S[p]&&S[p]>=S[p+1];} int main() { n=read(); for(int i=1;i<=n;++i) { S[++top]=read();sum+=S[top];vis[top]=(S[top]==0);fr^=(bool)(S[top]); while(top>=3&&check(top-1))S[top-2]=S[top-2]+S[top]-S[top-1],top-=2; } for(l=1;!vis[l]&&!vis[l+1]&&S[l]>=S[l+1];l+=2)ans+=(S[l]-S[l+1])*(fr?1:-1); for(r=top;!vis[r]&&!vis[r-1]&&S[r]>=S[r-1];r-=2)ans+=(S[r]-S[r-1])*(fr?1:-1); top=0;for(int i=l;i<=r;++i)if(!vis[i])S[++top]=S[i];sort(&S[1],&S[top+1]); for(int i=top;i;--i)ans+=((top-i)&1)?-S[i]:S[i]; cout<<(sum+ans)/2<<' '<<(sum-ans)/2<<endl; return 0; }
【BZOJ2000】[HNOI2000]取石頭遊戲(貪心,博弈論)