LOJ2834 「JOISC 2018 Day 2」修行

LOJ 传送门

考虑若已求出钦定 \(k\) 个升高的排列数量 \(f_k\),那么二项式反演就可以求出恰好 \(k\) 个升高的排列数量 \(g_k\),即:

\[g_k = \sum\limits_{i = k}^n (-1)^{i - k} \binom{i}{k} f_i \]

考虑求 \(f_i\)。相当于钦定原序列构成了 \(n - k\) 个上升段。相当于把 \(n\) 个数分成 \(n - k\)互相区分的集合,每个集合内部升序排序就是上升段。所以:

\[f_i = \begin{Bmatrix} n \\ n - i \end{Bmatrix} (n - i)! \]

直接 NTT 求一行的斯特林数可以通过 P5825 排列计数,但是这题的模数只能 MTT。考虑用斯特林数的通项公式展开,把 \(f_i\) 代入 \(g_k\)

\[\begin{aligned} g_k & = \sum\limits_{i = k}^n (-1)^{i - k} \binom{i}{k} \sum\limits_{j = 1}^{n - i} (-1)^{n - i - j} j^n \binom{n - i}{j} \\ & = \sum\limits_{j = 1}^n (-1)^{n + k + j} j^n \sum\limits_{i = k}^{n - j} \binom{i}{k} \binom{n - i}{j} \\ & = \sum\limits_{j = 1}^n (-1)^{n + k + j} j^n \binom{n + 1}{k + j + 1} \end{aligned} \]

最后一步用到了这个恒等式:\(\sum\limits_i \binom{i}{a} \binom{n - i}{b} = \binom{n + 1}{a + b + 1}\)。组合意义即为考虑 \(n + 1\) 个物品中选 \(a + b + 1\) 个,枚举第 \(a + 1\) 个物品的位置,加上左边选 \(a\) 个的方案数乘上右边选 \(b\) 个的方案数。

直接快速幂计算 \(i^n\) 就是 \(O(n \log n)\) 的,也可以线性筛做到 \(O(n)\)

code
// Problem: #2834. 「JOISC 2018 Day 2」修行
// Contest: LibreOJ
// URL: https://loj.ac/p/2834
// Memory Limit: 256 MB
// Time Limit: 600 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 = 100100;
const ll mod = 1000000007;

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, m, 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;
	}
}

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

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}
posted @ 2024-02-20 18:16  zltzlt  阅读(29)  评论(0编辑  收藏  举报