Problem - 1225G - Codeforces
一点想法
首先我们看这个神奇的函数 $f$。
如果说 $x = k^a\times b$ 的话,那么 $f(x) = b$。
我们考虑对于 $x = k^{a_1}b_1, y = k^{a_2}b_2, a_1 < a_2$。
我们可以得到 $(x + y) = k^{a_1}(k^{a_2-a_1}b_2 + b_1)$,显然右边部分肯定不是 $k$ 的倍数,是可以直接算出来的。
考虑题目中的限制是最终的答案为 $1$,那么意味着最后一次合并有 $k | x +y$ 。
注意到 $n = 16, k = 2000, \sum a_i \le 2000$,是不是可以进行状压。
复杂度貌似是 $O(2^n\sum a_i)$。考虑设 $f(S, i)$ 表示集合 $S$ 中的数能否拼出 $i$。
转移的时候枚举一下加入哪个数,复杂度是 $O(2^nn\sum a_i)$。是不是可以 $\tt bitset$。
考虑枚举转移位置和 $S$,我们貌似可以预处理出来转移矩阵,复杂度是 $\frac{n^2}{64} = 4$ 貌似优秀一点,就可以过了。
貌似对了。看看实现更加优秀的正解。
$\tt solution$
考虑 $\tt Dp$ 的本质,就是考虑对于若干个 $a_i$ 进行 $\div k^b_i$ 次操作,使得最终 $\sum_{i = 1} ^ n a_i \times k^{-b_i} = 1$。
考虑证明充分性:
如果有 $b$ 序列是可以推出合法解的,首先序列长为 $1, 2$ 是正确的,考虑对于 $n > 2$ 的时候,设 tmp = max(b[i])
,如果两个 tmp
可以合并被 $k$ 整除,那么直接归纳。不然继续合并,因为分母是 $k^{tmp}$ 最终答案是 $1$ 所以肯定是可以合并下去的,所以归纳成立。
那么我们现在是需要对于序列 $b$ 进行决策,根据我们证明我们是通过 $b_i$ 逐渐变小进行归纳的,所以需要对于 $b_i$ 从大到小进行计算。
设 $f(S, i)$ 表示集合 $S$ 最终能否是 $i$。
加入一个 $b_i = 0$,转移到 $f(S, i) \leftarrow f(nS, i - a_i)$。
进行 $\div K$ 操作,$f(S, i) \leftarrow f(S, ik)$。
后面这个使用完全背包即可,用 $\tt bitset$ 优化。
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 77 78 79 80 81 82 83
| #include <bits/stdc++.h> using namespace std; namespace Legendgod { namespace Read {
#ifdef Fread const int Siz = (1 << 21) + 5; char *iS, *iT, buf[Siz]; #define gc() ( iS == iT ? (iT = (iS = buf) + fread(buf, 1, Siz, stdin), iS == iT ? EOF : *iS ++) : *iS ++ ) #define getchar gc #endif template <typename T> void r1(T &x) { x = 0; char c(getchar()); int f(1); for(; !isdigit(c); c = getchar()) if(c == '-') f = -1; for(; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48); x *= f; } template <typename T, typename...Args> void r1(T &x, Args&...arg) { r1(x), r1(arg...); } #undef getchar }
using namespace Read;
const int maxn = 2e3 + 5; int n, K, sum(0), a[maxn], b[maxn]; bitset<maxn> f[(1 << 16) + 5]; priority_queue<pair<int, int>> q;
void dfs(int S,int c) { if(!S) return ; if(c * K <= sum && f[S][c * K]) { for(int i = 1; i <= n; ++ i) if((S >> (i - 1)) & 1) ++ b[i]; dfs(S, c * K); return ; } for(int i = 1; i <= n; ++ i) if((S >> (i - 1)) & 1) { int nS = (S ^ (1 << (i - 1))); if(a[i] <= c && f[nS][c - a[i]]) { dfs(nS, c - a[i]); return ; } } }
signed main() { int i, j; r1(n, K); for(i = 1; i <= n; ++ i) r1(a[i]), sum += a[i]; f[0][0] = 1; for(int S = 1; S < (1 << n); ++ S) { for(j = 1; j <= n; ++ j) if((S >> (j - 1)) & 1) f[S] |= f[S ^ (1 << (j - 1))] << a[j]; for(j = sum / K; j >= 1; -- j) f[S][j] = f[S][j] | f[S][j * K]; } int z = (1 << n) - 1; if(f[z][1] == 0) return puts("NO"), 0; puts("YES"); dfs(z, 1); for(i = 1; i <= n; ++ i) q.push({b[i], a[i]}); while(q.size() > 1) { auto u = q.top(); q.pop(); auto v = q.top(); q.pop(); printf("%d %d\n", u.second, v.second); int x = u.second + v.second, y = u.first; while(y && !(x % K)) x /= K, -- y; q.push({y, x}); } return 0; }
}
signed main() { return Legendgod::main(), 0; }
|