AtCoder Grand Contest 040 F Two Pieces

发布时间 2023-12-15 08:33:35作者: zltzlt

洛谷传送门

AtCoder 传送门

第二道问号题。

\(A \ge B\)。我们现在将点的坐标刻画到二维平面上。相当于找到一条 \((0, 0) \to (A, B)\) 的路径,要求不能跨过直线 \(y = x\)。有 \(3\) 种移动方式:

  1. 向右移动一格。
  2. 向上移动一格。
  3. 将当前点提到直线 \(y = x\) 上,也就是将 \(y\) 坐标赋值为 \(x\) 坐标。

为了避免算重,其中 \(2\) 操作要求向上移动一格后,点不能在直线 \(y = x\) 上。

若不使用操作 \(3\) 答案是好算的。相当于找到一条 \((1, 0) \to (A, B)\) 的不接触直线 \(y = x\) 的一条路径,考虑容斥,将任意一条与直线 \(y = x\) 有交的路径的最后一个交点拿出来,把这之前的部分沿直线 \(y = x\) 翻转,可以发现有交点的方案数就是 \((0, 1) \to (A, B)\) 的方案数。所以无交点的方案数是 \(\binom{A + B - 1}{A - 1} - \binom{A + B - 1}{A}\)

若使用操作 \(3\),考虑相对运动,将把点提到直线 \(y = x\) 上,变成将直线拉到 \(y = x - k\)。设在点 \((x_0, y_0)\) 最后一次使用操作 \(3\),设 \(k = x_0 - y_0\),那么我们的终点是 \((A, B - k)\),直线最终位于 \(y = x - k\)

考虑分配除了最后一次的操作 \(3\)。我们确定了一条 \((1, 0) \to (A, B - k)\) 的折线后,一个点 \((x, x - d)\) 可以使用操作 \(3\) 当且仅当这个点是直线 \(y = x - d\) 与折线的最后一个交点。发现 \(d = 0, 1, \ldots, k\) 都唯一对应一个点。于是我们把 \(n - A - (B - k) - 1\) 次操作 \(3\)(减 \(1\) 是因为 \(d = k\) 至少要使用一次操作 \(3\))分配到这 \(k + 1\) 个点上,插板算即可。

code
// Problem: F - Two Pieces
// Contest: AtCoder - AtCoder Grand Contest 040
// URL: https://atcoder.jp/contests/agc040/tasks/agc040_f
// Memory Limit: 1024 MB
// Time Limit: 4000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 20000100;
const int N = 20000000;
const ll mod = 998244353;

inline ll qpow(ll b, ll p) {
	ll res = 1;
	while (p) {
		if (p & 1) {
			res = res * b % mod;
		}
		b = b * b % mod;
		p >>= 1;
	}
	return res;
}

ll n, A, B, fac[maxn], ifac[maxn];

inline ll C(ll n, ll m) {
	if (n < m || n < 0 || m < 0) {
		return 0;
	} else {
		return fac[n] * ifac[m] % mod * ifac[n - m] % mod;
	}
}

inline ll F(ll n, ll m) {
	return (C(n + m - 1, n - 1) - C(n + m - 1, n) + mod) % mod;
}

void solve() {
	scanf("%lld%lld%lld", &n, &B, &A);
	fac[0] = 1;
	for (int i = 1; i <= N; ++i) {
		fac[i] = fac[i - 1] * i % mod;
	}
	ifac[N] = qpow(fac[N], mod - 2);
	for (int i = N - 1; ~i; --i) {
		ifac[i] = ifac[i + 1] * (i + 1) % mod;
	}
	if (A == 0 && B == 0) {
		puts("1");
		return;
	}
	ll ans = (A + B == n ? F(A, B) : 0);
	for (int i = 0; i <= B; ++i) {
		ll r = n - A - (B - i);
		ans = (ans + F(A, B - i) * C(r - 1 + i, i)) % mod;
	}
	printf("%lld\n", ans);
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}