像是游戏书的思维题
大方向是,用最少步骤得到一个确定的数y y y ,加上n − y n-y n − y 得到答案。
C1:由于初始x x x 范围巨大,先用 digit 操作是比较好的。发现两次 digit 操作的结果x ⩽ 16 x \leqslant 16 x ⩽ 1 6 ,再按二进制拆分,四次就能得到x = 1 x=1 x = 1 。总计6 + 1 = 7 6 + 1 = 7 6 + 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 = 1 0 9 − 1 N = 999999999 = 10^9 - 1 N = 9 9 9 9 9 9 9 9 9 = 1 0 9 − 1 。以下证明对于正整数n ⩽ 1 0 9 n \leqslant 10^9 n ⩽ 1 0 9 ,S ( N n ) = 81 S(Nn) = 81 S ( N n ) = 8 1 ,其中S ( x ) S(x) S ( x ) 表示x x x 的数码和。
证明:9 是最大的数位,因此S ( N − x ) = S ( N ) − S ( x ) = 81 − S ( x ) S(N-x)=S(N)-S(x)=81-S(x) S ( N − x ) = S ( N ) − S ( x ) = 8 1 − S ( x ) 对0 ⩽ x ⩽ N 0 \leqslant x \leqslant N 0 ⩽ x ⩽ N 都成立。这里已经出现了 81,我们尝试变形。
81 = S ( x ) + S ( N − x ) = S ( x ⋅ 1 0 9 ) + S ( N − x ) \begin{aligned} 81 &= S(x) + S(N-x) \\ &= S(x \cdot 10^{9}) + S(N-x) \end{aligned} 8 1 = S ( x ) + S ( N − x ) = S ( x ⋅ 1 0 9 ) + S ( N − x )
由于x ⋅ 1 0 9 x \cdot 10^{9} x ⋅ 1 0 9 的后九位都是 0,N − x N-x N − x 至多后九位,因此相加后互不冲突,即
81 = S ( x ⋅ 1 0 9 ) + S ( N − x ) = S ( x ⋅ 1 0 9 + N − x ) = S [ x ⋅ ( 1 0 9 − 1 ) + N ] = S ( N x + 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} 8 1 = S ( x ⋅ 1 0 9 ) + S ( N − x ) = S ( x ⋅ 1 0 9 + N − x ) = S [ x ⋅ ( 1 0 9 − 1 ) + N ] = S ( N x + N ) = S [ ( x + 1 ) N ]
再令y = x + 1 ∈ [ 1 , 1 0 9 ] y=x+1\in[1,10^{9}] y = x + 1 ∈ [ 1 , 1 0 9 ] ,就证明了S ( N y ) = 81 S(Ny)=81 S ( N y ) = 8 1 。
对每个顶点,BFS 计算奇数步和偶数步最短路。
考虑将所有可用的步长数字加起来,并判断奇偶性。如果 sum 是偶数就与偶数步最短路比较,否则 sum 是奇数就与奇数步最短路比较,sum 大于等于 dis 就可达。
或者,需要改变 sum 的奇偶性。最优策略是从 sum 中减去最小的奇数步长 minodd,得到新的总和 sum2,sum2 大于等于 dis 就可达。
复杂度O ( n + m + l ) \operatorname{O}(n+m+l) 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; ll sum = 0 ; while (l--) { int x; cin >> x; if (x & 1 ) { minodd = min <ll>(minodd, x); } sum += x; } ll sum2 = sum - minodd; 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); } 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 ; }
第i i i 位的操作次数只与后缀有关,考虑倒着 DP,设d p i , j dp_{i,j} d p i , j 表示对i i i 的后缀操作j j j 次的方案数。
考虑转移d p i , j ← d p i − 1 , k dp_{i,j}\leftarrow dp_{i-1,k} d p i , j ← d p i − 1 , k ,即第i i i 位处操作j − k j-k j − k 次的方案数。每次操作,第i i i 位都会翻转。如果s i = 0 s_{i}=0 s i = 0 ,只能是总共操作偶数次后才能操作第i i i 位。也就是说对于这个j j j 长的操作序列,操作第i i i 位只能放在第奇数位置上,方案数是( ⌈ j 2 ⌉ j − k ) \dbinom{\lceil \frac{j}{2} \rceil}{j-k} ( j − k ⌈ 2 j ⌉ ) 。如果s i = 1 s_{i}=1 s i = 1 ,就只能放在第偶数位置上,方案数是( ⌊ j 2 ⌋ j − k ) \dbinom{\lfloor \frac{j}{2} \rfloor}{j-k} ( j − k ⌊ 2 j ⌋ ) 。
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;