这是一个组合博弈问题。
这是什么小丑题!!!只要大力分讨就行了,结果我却写了整整 2 h + 2h+ 2h+!!!
首先考虑 K = 1 K=1 K=1的经典模型。设序列长度为 N N N,记 p = n 2 p=\frac{n}{2} p=2n,给出下列结论(假设先手先取):
1.1 1.1 1.1 若 N N N为偶数,答案 max ( a p , a p + 1 ) \max(a_{p},a_{p+1}) max(ap,ap+1)。
1.2 1.2 1.2 若 N N N为奇数,答案 max ( min ( a p − 1 , a p ) , min ( a p , a p + 1 ) ) \max(\min(a_{p-1},a_p),\min(a_p,a_{p+1})) max(min(ap−1,ap),min(ap,ap+1))。
后手先取的情况是对称的就不写了。
这肯定是可以通过 D P DP DP来严格证明的,但是也可以不严谨的感性理解一下:如果先手和后手都能保证取到一个相同的数 M M M,那么在双方都采取最优策略的情况下剩下的那个数就是 M M M。
然后有一个结论:如果 N N N为奇数那么后手取比先手取更优。换句话说,如果一个序列为奇数那么谁也不会去动它,因为动它就说明自己先手取,这样就不优了。因此两个玩家都会选长度为偶数并且没有被操作过的序列进行操作,这样就会剩下若干个长度为奇数的序列,根据长度为偶数的序列的数目的奇偶性可以确定此时应该先手取还是后手取。直接模拟即可。
为什么这样是对的?假设对手把一个长度为偶数的序列取成了奇数,那么如果跟的话对手也会跟,这样相当于还是相当于先手在取一个长度为奇数的序列,这样的决策也是不优的。
复杂度 O ( n log n ) O(n\log n) O(nlogn)。需要排序,以及分类讨论。
#include
#define ll long long
#define fi first
#define se second
#define pb push_back
#define db double
using namespace std;
int K,n;
ll res;
vector<int>nums[200005];
vector<int>values;
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>K;
for(int i=1;i<=K;i++){
int len;cin>>len;
for(int j=0;j<len;j++){
int x;cin>>x,nums[i].pb(x);
}
if(len==1){
res+=nums[i][0];
continue;
}
if(len%2==0){
n++;
}
}
for(int i=1;i<=K;i++){
int len=nums[i].size();
if(len%2==0){
if(len==2){
int x=min(nums[i][0],nums[i][1]);
int y=max(nums[i][0],nums[i][1]);
assert(y>=x);
res+=x,values.pb(y-x);
}
else if(n%2==0){
int x=nums[i][len/2-2],y=nums[i][len/2-1],z=nums[i][len/2],w=nums[i][len/2+1];
int val1=max(min(x,y),min(y,z));
int val2=max(min(y,z),min(z,w));
if(val1>val2)swap(val1,val2);
res+=val1,values.pb(val2-val1);
}
else{
int x=nums[i][len/2-2],y=nums[i][len/2-1],z=nums[i][len/2],w=nums[i][len/2+1];
int val1=min(max(x,y),max(y,z));
int val2=min(max(y,z),max(z,w));
if(val1>val2)swap(val1,val2);
res+=val1,values.pb(val2-val1);
}
}
}
sort(values.begin(),values.end());
reverse(values.begin(),values.end());
for(int i=0;i<values.size();i+=2){
res+=values[i];
}
for(int i=1;i<=K;i++){
int len=nums[i].size();
if(len>1&&len%2==1){
int x=nums[i][len/2-1],y=nums[i][len/2],z=nums[i][len/2+1];
if(n%2==0){
res+=max(min(x,y),min(y,z));
}
else{
res+=min(max(x,y),max(y,z));
}
}
}
cout<<res;
}