2109C - Hacking Numbers

像是游戏书的思维题

大方向是,用最少步骤得到一个确定的数yy,加上nyn-y 得到答案。

C1:由于初始xx 范围巨大,先用 digit 操作是比较好的。发现两次 digit 操作的结果x16x \leqslant 16,再按二进制拆分,四次就能得到x=1x=1。总计6+1=76 + 1 = 7 次。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int n;
cin >> n;
int x;
cout << "digit" << endl;
cin >> x;
cout << "digit" << endl;
cin >> x;
cout << "add " << -8 << endl;
cin >> x;
cout << "add " << -4 << endl;
cin >> x;
cout << "add " << -2 << endl;
cin >> x;
cout << "add " << -1 << endl;
cin >> x;
cout << "add " << n - 1 << endl;
cin >> x;
cout << "!" << endl;
cin >> x;

C2:不好解释。。敲了半小时计算器发现的

1
2
3
4
5
6
7
8
9
10
11
12
13
int n;
cin >> n;
int x;
cout << "mul " << 9 << endl;
cin >> x;
cout << "digit" << endl;
cin >> x;
cout << "digit" << endl;
cin >> x;
cout << "add " << n - 9 << endl;
cin >> x;
cout << "!" << endl;
cin >> x;

C3:呃,比较熟知的结果是 7 * 9 = 63,7 * 99,999 = 699,993,想到这一点应该就能试出来

1
2
3
4
5
6
7
8
9
10
11
12
13
int n;
cin >> n;
int x;
cout << "mul " << 999999999 << endl;
cin >> x;
cout << "digit" << endl;
cin >> x;
if (n - 81) {
cout << "add " << n - 81 << endl;
cin >> x;
}
cout << "!" << endl;
cin >> x;

脑电波题蛙,作为算法竞赛题真合适吗

给个证明:设N=999999999=1091N = 999999999 = 10^9 - 1。以下证明对于正整数n109n \leqslant 10^9S(Nn)=81S(Nn) = 81,其中S(x)S(x) 表示xx 的数码和。

证明:9 是最大的数位,因此S(Nx)=S(N)S(x)=81S(x)S(N-x)=S(N)-S(x)=81-S(x)0xN0 \leqslant x \leqslant N 都成立。这里已经出现了 81,我们尝试变形。

81=S(x)+S(Nx)=S(x109)+S(Nx)\begin{aligned} 81 &= S(x) + S(N-x) \\ &= S(x \cdot 10^{9}) + S(N-x) \end{aligned}

由于x109x \cdot 10^{9} 的后九位都是 0,NxN-x 至多后九位,因此相加后互不冲突,即

81=S(x109)+S(Nx)=S(x109+Nx)=S[x(1091)+N]=S(Nx+N)=S[(x+1)N]\begin{aligned} 81 &= S(x \cdot 10^{9}) + S(N-x) \\ &= S(x \cdot 10^{9} + N-x) \\ &= S[x \cdot (10^{9}-1) + N] \\ &= S(Nx+N) \\ &= S[(x+1)N] \end{aligned}

再令y=x+1[1,109]y=x+1\in[1,10^{9}],就证明了S(Ny)=81S(Ny)=81

2109D - D/D/D(奇偶分层图)

对每个顶点,BFS 计算奇数步和偶数步最短路。

考虑将所有可用的步长数字加起来,并判断奇偶性。如果 sum 是偶数就与偶数步最短路比较,否则 sum 是奇数就与奇数步最短路比较,sum 大于等于 dis 就可达。

或者,需要改变 sum 的奇偶性。最优策略是从 sum 中减去最小的奇数步长 minodd,得到新的总和 sum2,sum2 大于等于 dis 就可达。

复杂度O(n+m+l)\operatorname{O}(n+m+l)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

constexpr ll inf = 0x3f3f3f3f3f3f3f3f;

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);

int t;
cin >> t;

while (t--) {
int n, m, l;
cin >> n >> m >> l;

ll minodd = inf; // inf > sum of all elements
ll sum = 0;
while (l--) {
int x;
cin >> x;
if (x & 1) {
minodd = min<ll>(minodd, x);
}
sum += x;
}
ll sum2 = sum - minodd; // When there are no odd numbers, sum2 < 0

vector<vector<int>> E(2 * n);
auto add = [&](int u, int v) {
E[u].push_back(v);
E[v].push_back(u);
};
while (m--) {
int u, v;
cin >> u >> v;
u--;
v--;
add(u, v + n);
add(u + n, v);
}

// BFS
vector<int> dis(2 * n, -1);
dis[0] = 0;
queue<int> Q;
Q.push(0);
while (!Q.empty()) {
int u = Q.front();
Q.pop();
for (auto& v : E[u]) {
if (dis[v] != -1) continue;
dis[v] = dis[u] + 1;
Q.push(v);
}
}

string res(n, '0');
for (int i = 0; i < 2 * n; i++) {
if (dis[i] == -1) continue;
if (dis[i] % 2 == sum % 2) {
if (dis[i] <= sum) {
res[i % n] = '1';
}
} else {
if (dis[i] <= sum2) {
res[i % n] = '1';
}
}
}
cout << res << endl;
}

return 0;
}

2109E - Binary String Wowee

ii 位的操作次数只与后缀有关,考虑倒着 DP,设dpi,jdp_{i,j} 表示对ii 的后缀操作jj 次的方案数。

考虑转移dpi,jdpi1,kdp_{i,j}\leftarrow dp_{i-1,k},即第ii 位处操作jkj-k 次的方案数。每次操作,第ii 位都会翻转。如果si=0s_{i}=0,只能是总共操作偶数次后才能操作第ii 位。也就是说对于这个jj 长的操作序列,操作第ii 位只能放在第奇数位置上,方案数是(j2jk)\dbinom{\lceil \frac{j}{2} \rceil}{j-k}。如果si=1s_{i}=1,就只能放在第偶数位置上,方案数是(j2jk)\dbinom{\lfloor \frac{j}{2} \rfloor}{j-k}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int N, K;
string S;
cin >> N >> K >> S;

vector<Z> f(K + 1);
f[0] = 1;
for (int i = N - 1; i >= 0; i--) {
vector<Z> nf(K + 1);
for (int j = 0; j <= K; j++) {
for (int k = 0; k <= j; k++) {
nf[j] += f[k] * binom((j + (S[i] == '0')) / 2, j - k);
}
}
f = nf;
}
cout << f[K] << endl;