题解-AGC022F Checkers
题意
令 \(x=10^{100}\),数轴上有 \(n\) 个点,第 \(i\) 个点的坐标为 \(x^i\),进行 \(n-1\) 次操作, 每次操作选择两点 \(A\) 和 \(B\), 将 \(A\) 移动到 \(A\) 关于 \(B\) 的对称的位置并删去 \(B\)。求最后剩下的一个数有多少种可能的取值。
数据范围:\(n \le 50\)。
题解
对于两个点 \((x,y)\),他们生成的点坐标是 \(2y-x\)。所以可以发现最后每个数的贡献一定是 \(2^k x^i\) 或 \(-2^k x^i\) 的形式。由于 \(x\) 很大,所以一个数的 \(k\) 不同,最后的答案就不同。
那么我们现在的一次操作就可以看作是每次选择两个数 \(w2^{k+1},-w2^k (w \in \{1, -1\})\),并把他们变成 \(w2^k\)。
这样我们就可以按照 \(k\) 从大往小 DP 了。\(dp_{i,u,v}\) 表示我们现在用过了 \(i\) 个数,这一层的 \(1\) 有 \(u\) 个,这一层的 \(-1\) 有 \(v\) 个。最后要提取的就是 \(dp_{n,1,0}\)。
然后我们枚举下一层的 \(1\) 和 \(-1\) 选了多少个,设为 \(x,y\)。我们不妨 \(u \ge v\)。
这样我们的一个 \(1\) 可以消除 \(u + 1\) 个 \(1\) 和 \(v\) 个 \(-1\),并变成 \(-1\)。消除 \(u-v\) 次就可以把 \(u - v\) 个 \(1\) 变成 \(-1\),并把上一层的点清光。(如果 \(u = v\) 并且 \(x = 0\) 也是没关系的,这样 \(y\) 必然 \(> 0\),所以用 \(-1\) 即可,最后返回的还是 \(-1\))。
这样消除是优的,因为这只要求了原先有 \(u - v\) 个 \(1\),我们就删了 \(u - v\) 个 \(1\),而最后剩下的 \(1\) 和 \(-1\) 的个数是固定的。
于是直接 DP 就是 \(\Theta(n^5)\) 的。其实已经可以过了。
我们只需要用到 \(u - v\) 的值,所以我们可以压掉一维。而我们又发现原先的 DP 中 \(dp_{i,x,y} = dp_{i,y,x}\),所以就只用记录 \(u - v \ge 0\) 的了。
这样时间复杂度是 \(\Theta(n^4)\)。
代码
#include<bits/stdc++.h>
#define L(i, j, k) for(int i = (j); i <= (k); ++i)
#define R(i, j, k) for(int i = (j); i >= (k); --i)
#define ll long long
#define vi vector <int>
#define sz(a) ((int) (a).size())
#define me(f, x) memset(f, x, sizeof(f))
using namespace std;
const int N = 55, mod = 1e9 + 7, inv2 = (mod + 1) / 2;
int n, dp[N][N], ns;
int qpow(int x, int y = mod - 2) {
int res = 1;
for(; y; x = (ll) x * x % mod, y >>= 1) if(y & 1) res = (ll) res * x % mod;
return res;
}
int fac[N], ifac[N], inv[N];
void init(int x) {
fac[0] = ifac[0] = inv[1] = 1;
L(i, 2, x) inv[i] = (ll) (mod - mod / i) * inv[mod % i] % mod;
L(i, 1, x) fac[i] = (ll) fac[i - 1] * i % mod, ifac[i] = (ll) ifac[i - 1] * inv[i] % mod;
}
int C(int x, int y) {
return x < y || y < 0 ? 0 : (ll) fac[x] * ifac[y] % mod * ifac[x - y] % mod;
}
int main () {
ios :: sync_with_stdio(false);
cin.tie (0); cout.tie (0);
cin >> n;
init (n);
dp[0][0] = 1;
L(i, 0, n) {
L(x, 0, n) if(dp[i][x]) {
int up = n - i, dt = x;
L(u, 0, up) L(v, dt, up) if(u + v && u + v <= up) {
if(u + v == up && up == 1) {
(ns += (ll) dp[i][x] * ifac[u] % mod * ifac[v] % mod) %= mod;
} else {
(dp[i + u + v][abs(u + dt - v + dt)] +=
(ll) dp[i][x] * ifac[u] % mod * ifac[v] % mod) %= mod;
}
}
}
}
cout << (ll) ns * fac[n] % mod * inv2 % mod << '\n';
return 0;
}