AtCoder Regular Contest 116 F Deque Game

洛谷传送门

AtCoder 传送门

很强的博弈 + 性质题。下文令 A 为 Takahashi,B 为 Aoki。

发现单独考虑一个序列 a1,a2,...,an

  • nmod2=0
    • 若 A 为先手,答案为 max(an2,an2+1)
    • 若 B 为先手,答案为 min(an2,an2+1)(由于 minmax 的对称性)。
    • 双方都是成为先手比成为后手优。
  • nmod2=1
    • 若 A 为先手,答案为 min(an+12,max(an+121,an+12+1))
    • 若 B 为先手,答案为 max(an+12,min(an+121,an+12+1))
    • 双方都是成为后手比成为先手优。

相同的结论在 CF794E 也出现过。这是容易归纳证明的,故证明略。

考虑如果全部序列长度都为奇数,容易发现若先手取了其中一个序列的一个数,后手肯定再把这个序列的长度取回奇数。这是因为如果出现了长度为偶数的序列,那它肯定会成为先后手争夺的对象(双方都是成为先手比成为后手优)。所以全部序列的答案可以独立计算再加起来。

现在有一些序列长度为偶数,根据前面的讨论,它们会成为双方争夺的对象。取完偶数的序列后,就回到了上面的情况,并且先后手可能会交换顺序。设 x,y 为对于一个偶长序列,取头部和取尾部得到的剩下的序列的答案,设 mx=max(x,y),mn=min(x,y),发现肯定是按 mxmn 从大到小先手交替地取。因为如果是 A 取了它,它会比 B 取它多了 mxmn 的得分,反之如果 B 取了它,它会比 A 取它少 mxmn 的得分。

这样最后的答案由两部分组成,一部分是奇长序列的答案,由于可以单独考虑,所以可以提前确定;另一部分是偶长序列的答案,需要先后手交替取完后转化为奇长序列再记入答案。

时间复杂度线性对数,瓶颈在于排序。

code
// Problem: F - Deque Game
// Contest: AtCoder - AtCoder Regular Contest 116
// URL: https://atcoder.jp/contests/arc116/tasks/arc116_f
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 200100;
ll n;
vector<ll> vc[maxn];
inline bool cmp(pii a, pii b) {
return a.fst - a.scd > b.fst - b.scd;
}
inline ll calc(vector<ll> &a, int op) {
ll m = (ll)a.size();
if (!m) {
return 0;
} else if (m == 1) {
return a[0];
}
if (op) {
if (m & 1) {
return min(a[m / 2], max(a[m / 2 - 1], a[m / 2 + 1]));
} else {
return max(a[m / 2], a[m / 2 - 1]);
}
} else {
if (m & 1) {
return max(a[m / 2], min(a[m / 2 - 1], a[m / 2 + 1]));
} else {
return min(a[m / 2], a[m / 2 - 1]);
}
}
}
void solve() {
scanf("%lld", &n);
int c = 0;
for (int i = 1, k, x; i <= n; ++i) {
scanf("%d", &k);
while (k--) {
scanf("%d", &x);
vc[i].pb(x);
}
c += (vc[i].size() % 2 == 0);
}
vector<pii> S;
ll ans = 0;
if (c & 1) {
for (int i = 1; i <= n; ++i) {
if (vc[i].size() & 1) {
ans += calc(vc[i], 0);
continue;
}
auto a = vc[i], b = vc[i];
a.erase(a.begin());
b.pop_back();
ll x = calc(a, 0), y = calc(b, 0);
S.pb(max(x, y), min(x, y));
}
} else {
for (int i = 1; i <= n; ++i) {
if (vc[i].size() & 1) {
ans += calc(vc[i], 1);
continue;
}
auto a = vc[i], b = vc[i];
a.erase(a.begin());
b.pop_back();
ll x = calc(a, 1), y = calc(b, 1);
S.pb(max(x, y), min(x, y));
}
}
sort(S.begin(), S.end(), cmp);
for (int i = 0, o = 1; i < (int)S.size(); ++i, o ^= 1) {
ans += (o ? S[i].fst : S[i].scd);
}
printf("%lld\n", ans);
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}
posted @   zltzlt  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示