CodeForces 1839E Decreasing Game
不会,不知道该如何评价。确实是自己的问题。
这种题肯定考虑先 / 后手必胜的充要条件。
直接给出结论:若 \(a\) 能被分成两个和相等的可重集,后手必胜,否则先手必胜。
感性理解一下,如果 \(a\) 能被分成两个和相等的可重集,先手选一个数,后手选另一个可重集中的数。因为两个可重集的和相等,所以一定不会出现后手没法选的情况。
如果不能,先手随便选即可。因为后手无法操作也无法使接下来的 \(a\) 能被分成两个和相等的可重集。
然后随便 \(O(n \sum\limits_{i = 1}^n a_i)\) dp 一下找到分组方案即可。
code
// Problem: E. Decreasing Game
// Contest: Codeforces - Codeforces Round 876 (Div. 2)
// URL: https://codeforces.com/problemset/problem/1839/E
// Memory Limit: 256 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 double db;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 310;
int n, a[maxn], f[maxn][maxn * maxn], g[maxn][maxn * maxn], b[maxn];
void solve() {
scanf("%d", &n);
int s = 0;
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
s += a[i];
}
f[0][0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 0; j <= s; ++j) {
if (j >= a[i] && f[i - 1][j - a[i]]) {
f[i][j] = 1;
g[i][j] = 1;
}
if (f[i - 1][j]) {
f[i][j] = 1;
g[i][j] = 2;
}
}
}
if ((s & 1) || !f[n][s / 2]) {
puts("First");
fflush(stdout);
while (1) {
int x = -1, y = -1;
for (int i = 1; i <= n; ++i) {
if (a[i]) {
x = i;
break;
}
}
printf("%d\n", x);
fflush(stdout);
scanf("%d", &y);
if (y <= 0) {
break;
}
int mn = min(a[x], a[y]);
a[x] -= mn;
a[y] -= mn;
}
} else {
puts("Second");
fflush(stdout);
for (int i = n, j = s / 2; i; --i) {
if (g[i][j] == 1) {
b[i] = 1;
j -= a[i];
} else {
b[i] = 2;
}
}
while (1) {
int x, y = -1;
scanf("%d", &x);
if (x <= 0) {
break;
}
for (int i = 1; i <= n; ++i) {
if (a[i] && b[i] != b[x]) {
y = i;
break;
}
}
printf("%d\n", y);
fflush(stdout);
int mn = min(a[x], a[y]);
a[x] -= mn;
a[y] -= mn;
}
}
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}