Luogu3210 [HNOI2010]取石頭遊戲
https://www.luogu.com.cn/problem/P3210
貪心
我們維護先手得分與後手得分的差值
結論:
在任何情況下,如果能夠取到整個序列的最大值,那麼取到整個序列的最大值一定最優
證明:
假設有一個對手,與你同時下同一種局面下的兩個遊戲
在第一局中,對手執先手,在第二種局面中,你執先手
你的策略是上述結論,你只需要保證第二種局面不會比第一種更劣就好了
\(Round 1\)
第一局:
對手\(p_1\):\(x\)
你\(q_1\):\(Max\)
第二局:
你\(q_2\):\(Max\)
對手\(p_2\):\(y\)
接下來的幾輪,你就模仿對手就好了,對手取啥,你就取啥
有時你取不到對手取的東西,你就取對手取的位置旁邊的\(0\)
這樣保證滿足:\(q_2=p_1-\{x^{'}\}+\{M\},q_1=p_2-\{y^{'}\}+\{M\}\)(注:\(x^{'},y^{'}\)不一定是\(x,y\))
觀察上面的式子,你驚奇地發現你總是更優
一旦在一輪兩局中出現相同的局面,就可以直接結束(你可以完全模仿對方啦!)
證明完畢!
但是\(Max\)不一定能夠取到,我們先考慮一下簡化序列
對於序列\(x,y,z(x<y,z<y)\),先後手肯定都想取\(z\),所以沒人會先取,除非不得已
如果一定要取,一定是不得已了,那麼一定會一口氣取完,先取的產生貢獻\(x+z-y\)
這樣我們可以把所有序列(中間用\(0\)
對於不在兩端的序列,我們隨時可以取得它的最大值,一個個取就是最優解
對於首尾的序列,對於首單調遞增的那部分,尾單調遞減的那部分,我們可以按照上面的方法處理
麻煩的是剩餘的那部分(這裡包括山谷),考慮一下,會不會在不在兩端的序列還沒取完的時候,就有人去取它了呢?
假設先手取了,那麼說明在如果最後自動分配時,這個數是後手取的(對先手不利)
那麼顯然,後手不會繼續取(隔一個取的)
根據奇偶性,後手依舊可以取到相同奇偶性的較優秀的方案
所以,我們只需要把山谷也當成可以直接取的來處理就好了
\(OK!\)
\(C++ Code:\)
#include<iostream> #include<cstdio> #include<algorithm> #include<queue> #define N 1000005 #define _INF -123456789987654321 #define ll long long using namespace std; int cnt,_n,n,L,R,RL,RR; ll _s,s=0,st,ans=0,x,a[N]; priority_queue<ll>q; int main() { scanf("%d",&_n); L=_n+1,R=0; for (int i=1;i<=_n;i++) { scanf("%lld",&x); s+=x; if (!x) { a[++n]=_INF; continue; } a[++n]=x; while (n>2 && a[n-1]!=_INF && a[n-2]!=_INF && a[n-2]<a[n-1] && a[n-1]>a[n]) { a[n-2]=a[n-2]+a[n]-a[n-1]; n-=2; } } for (int i=1;i<=n;i++) if (a[i]!=_INF) cnt++; for (L=1;a[L]!=_INF && a[L+1]!=_INF && a[L]>=a[L+1];L+=2) { if (cnt & 1) ans+=a[L]-a[L+1]; else ans+=a[L+1]-a[L]; } for (R=n;a[R]!=_INF && a[R-1]!=_INF && a[R]>=a[R-1];R-=2) { if (cnt & 1) ans+=a[R]-a[R-1]; else ans+=a[R-1]-a[R]; } for (;L<=R;L++) if (a[L]!=_INF) q.push(a[L]); st=1; while (!q.empty()) { ans+=st*q.top(); q.pop(); st=-st; } printf("%lld %lld\n",(s+ans) >> 1,(s-ans) >> 1); return 0; }