【ARC116F】Deque Game 题解 (找规律)
找规律。
Solution
手玩样例极其重要。
1
通过手玩小样例可以发现:
- 若数列长度
为奇数( ):高桥先手,答案为 ;若青木先手,答案为 。 - 反之(
):高桥先手,答案为 ;青木先手,答案为 。
由此,我们得到(手玩样例也可以得到):
- 若序列长度为奇数,则每个人都希望自己是后手;
- 若序列长度为偶数,则每个人都希望自己是先手。
综上,我们知道:如果一个长度为奇数的序列,已知操作它的先手是谁,那么就可以直接得到它最后剩余的那个数。
因为偶长序列先手更优,所以两人应在开始时去抢偶长序列,让自己成为某些偶长序列的先手,使得自己在这些偶长序列中收益最大。
2
所以,他们的行动方式应该是:
-
两个人轮流 在当前局面内 长度为偶数的序列中 挑选一个序列取走其首项或末项,将它改为一个长度为奇数的序列。
而被挑选的序列满足:假设一个偶长序列先取走首项后剩下的值为
,先取末项的剩余值是 ,则有 。那么这个被取序列一定是当前所有的长度为偶数的序列中, 的值最大的一个偶长序列。为什么这样能保证最优?对高桥而言,若取它,则会比青木取它让结果总值多出
个值;对青木而言,若取它,则会比高桥取它让结果总值少掉 个值。 -
当所有偶长序列都被更改为奇长序列之后,接下来没有偶长序列可取的人不得不成为剩下所有奇长序列的先手。又因为此时所有的奇长序列的剩余值都可以提前预处理出(对于每个奇长序列分别有两种预处理情况:高桥先手和青木先手)。故最后相加先手所对应的答案即可。
Code
贴上 @fjy666 大佬的详细注释代码。
#include <bits/stdc++.h>
using namespace std;
#define _rep(i_,a_,b_) for(int i_ = a_; i_ <= b_; ++i_)
#define mid ((L+R) >> 1)
#define get(x) (((x) - 1) / kb + 1)
#define multiCase() int testCnt = in(); _rep(curCase,1,testCnt)
typedef long long ll;
int in(void) { int x; scanf("%d", &x); return x; }
ll inl(void) { ll x; scanf("%lld", &x); return x; }
template<typename T> void chkmax(T &a, const T &b) { a = max(a, b); }
template<typename T> void chkmin(T &a, const T &b) { a = min(a, b); }
const int kN = 200500;
int a[kN], n;
//以下使用 0 代表 MINer, 1 代表 MAXer
//返回如果操作区间为 [L,R],先操作者为 opt,最后会剩下啥
int f(int L, int R, int opt) {
if(L == R) return a[L]; //如果长度为1那么谁也不能拿了
if((R - L + 1) & 1) //奇数区间
return opt ?
max(f(L, R - 1, opt ^ 1), f(L + 1, R, opt ^ 1))
:min(f(L, R - 1, opt ^ 1), f(L + 1, R, opt ^ 1));
else return opt ? max(a[mid], a[mid + 1]) : min(a[mid], a[mid + 1]);
}
int evcnt;
struct Game { //一局长为偶数的游戏
int mx, mn;
//mx:如果MAXer先动手改成 ODD 留下的数目
//mn:如果MINer先动手改成 ODD 留下的数目
void init(int mxer, int mner) {
mx = mxer, mn = mner;
if(mx < mn) swap(mx, mn); //MAXer希望答案最大
}
bool operator > (const Game &rhs) const {
return mx - mn > rhs.mx - rhs.mn; //按照差值来排序
}
} EVEN[2][kN];
//EVEN[0][j].mx 代表如果最后是 MINer 先取 已经被 MAXer 改成奇数 的这堆,能取到多少
//EVEN[0][j].mn 代表如果最后是 MINer 先取 已经被 MINer 改成奇数 的这堆,能取到多少
//EVEN[1][j].mx 代表如果最后是 MAXer 先取 已经被 MAXer 改成奇数 的这堆,能取到多少
//EVEN[1][j].mn 代表如果最后是 MAXer 先取 已经被 MINer 改成奇数 的这堆,能取到多少
//因为流程是这样的:
//首先 MINer 和 MAXer 把所有的 EVEN 局面改成 ODD 局面
//改完之后开始处理所有 ODD 局面
int main() {
int k = in();
ll mn = 0, mx = 0, res = 0;
while(k--) {
n = in();
_rep(i,1,n) a[i] = in();
if(n & 1) {
mn += f(1, n, 0), mx += f(1, n, 1);
} else { //偶数,分类讨论
++evcnt;
_rep(j, 0, 1) EVEN[j][evcnt].init(f(1, n - 1, j), f(2, n, j));
}
}
if(!(evcnt & 1)) { //如果有偶数个“长为偶数的数组”
res += mx; //最后是MAXer动手取ODD
int opt = (evcnt & 1) ^ 1; //opt = 1
sort(EVEN[opt] + 1, EVEN[opt] + 1 + evcnt, greater<Game>());
_rep(i,1,evcnt) res += (i & 1) ? EVEN[opt][i].mx : EVEN[opt][i].mn;
//按差值排序后,由于是MAXer先动的手,所以要交替取
} else {
res += mn; //最后是MINer先动手取 ODD
int opt = (evcnt & 1) ^ 1;
sort(EVEN[opt] + 1, EVEN[opt] + 1 + evcnt, greater<Game>());
_rep(i,1,evcnt) res += (i & 1) ? EVEN[opt][i].mx : EVEN[opt][i].mn;
}
printf("%lld\n", res);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通