Kick Start 2019 Round H. Elevanagram
设共有 \(N = \sum\_{i=1}^{9} A_i\) 个数字。先把 \(N\) 个数字任意分成两组 \(A\) 和 \(B\),\(A\) 中有 \(N_A = \floor{N/2}\) 个数字,\(B\) 中有 \(N_B=\ceil{N/2}\) 个数字。将 \(A\) 中数字之和记作 \(S_A\),\(B\) 中数字之和记作 \(S_B\)。若 \((S_A - S_B) \bmod 11 \ne 0\),再进行调整。
考虑通过交换 \(A\)、\(B\) 中的数字来改变 \(S_A - S_B\) 的值。若有解一定能通过若干次交换操作使得 \(S_A - S_B \bmod 11 = 0\)。用 \((a, b)\) 表示将 \(A\) 中的数字 \(a\) 与 \(B\) 中的数字 \(b\) 进行交换,交换 \(a, b\) 之后 \(S_A - S_B\) 的变化量是 \(-2(a - b)\)。
注意到若存在两个数字 \(i, j\) 都出现了至少 \(10\) 次,则答案一定是 YES。因为当 \(k\) 取遍 \(1\) 到 \(10\),\(2k(i - j)\) 也取遍 \(1\) 到 \(10\)(在模 \(11\) 的意义下)。
若每个数字都出现了不到 \(10\) 次,可以用 DP 解决。令 \(f[i][j][k]\) 表示从 \(A\) 中取出 \(1, 2, \dots, i\) 这 \(i\) 种数字共 \(j\) 个且取出的数字之和模 \(11\) 等于 \(k\) 是否有可能。对 \(B\) 也进行同样的 DP。
补充:看了官方题解之后发现这个 DP 不必分别对 \(A\) 做一次再对 \(B\) 做一次。可以只对 \(A\) 做一次 DP,方法是把 DP 的第三维改成前 \(i\) 个数字造成的差值模 \(11\) 等于 \(k\)。如此定义 DP 状态,最后答案就是 \(f[9][\floor{N/2}][0]\)。下面的 DP 也可以如此改造。
考虑只有一个数字出现了至少 \(10\) 次的情形。设此数字是 \(d\)。设 \(d\) 在 \(A\) 中出现了 \(d_A\) 次,在 \(B\) 中出现了 \(d_B\) 次。
可以证明任意操作序列 \((a_1, b_1), \dots, (a_n, b_n)\) 都可以化成等价的操作序列 \((a'_1, b'_1), \dots, (a'_k, b'_k)\) 且满足对于任意 \(1 \le i, j \le k\),\(a'_i \ne b'_j\)。
由于一定存在 \(d\) 只作为 \(a_i\) 或只作为 \(b_i\) 的操作序列,我们只需考虑长度不超过 \(t = \max\\{N\_A - d\_A, N\_B - d\_B\\}\) 的操作序列。于是我们可以将 \(A\)、\(B\) 中 \(1\) 到 \(9\) 的每个数字取出至多 \(t\) 个,对这两组数目较少的数字进行 DP。
int main() {
int T;
scan(T);
rep (T) {
kase();
vi a(10);
up (i, 1, 9) scan(a[i]);
int n10 = 0; //出现次数大于等于10的数字的个数
up (i, 1, 9) {
n10 += a[i] >= 10;
}
if (n10 >= 2) {
println("YES");
continue;
}
ll n = accumulate(all(a), 0LL);
ll m = n / 2;
vi b(10);
// 先随机分配,再通过DP判断能否进行调整。
up (i, 1, 9) {
if (a[i] < m) {
swap(a[i], b[i]);
m -= b[i];
} else {
b[i] = (int)m;
a[i] -= (int)m;
break;
}
}
ll sa = 0, sb = 0;
up (i, 1, 9) {
sa += a[i] * i;
sb += b[i] * i;
}
ll r = (sa - sb) % 11;
if (r == 0) {
println("YES");
continue;
}
if (r < 0) {
r += 11;
}
// r = r / 2;
// (11 + 1) / 2 是 2 在模 11 下的拟元。
r = r * (11 + 1) / 2 % 11;
// dp[i][j][k] 在前i种数里选择j个数余数是否可能是k
// 用滚动数组去掉dp数组第一维。
int na = 0, nb = 0;
up (i, 1, 9) {
if (a[i] < 10) {
na += a[i];
}
if (b[i] < 10) {
nb += b[i];
}
}
auto t = max(na, nb);
up (i, 1, 9) {
a[i] = min(a[i], t);
b[i] = min(b[i], t);
}
na = accumulate(all(a), 0);
nb = accumulate(all(b), 0);
vv<int> A(na + 1, vi(11));
vv<int> B(nb + 1, vi(11));
A[0][0] = 1;
B[0][0] = 1;
int ca = 0, cb = 0;
up (i, 1, 9) {
if (a[i] > 0) {
down (j, ca, 0) {
rng (k, 0, 11) {
if (A[j][k]) {
up (l, 1, a[i]) {
A[j + l][(k + l * i) % 11] = true;
}
}
}
}
ca += a[i];
}
if (b[i] > 0) {
down (j, cb, 0) {
rng (k, 0, 11) {
if (B[j][k]) {
up (l, 1, b[i]) {
B[j + l][(k + l * i) % 11] = true;
}
}
}
}
cb += b[i];
}
}
bool flag = false;
up (i, 1, min(na, nb)) {
rng (j, 0, 11) {
if (B[i][j] && A[i][(j + r) % 11]) {
flag = true;
break;
}
}
if (flag) break;
}
println(flag ? "YES" : "NO");
}
return 0;
}