Loading

题解-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;
}
posted @ 2022-01-22 11:35  zhoukangyang  阅读(482)  评论(0编辑  收藏  举报