题解:P11767 「KFCOI Round #1」缥缈
写一篇详细一点的题解。
注:本篇文章使用 deepseek 润色。
题意转化
设选出的
即所有元素必须落在某个长度不超过
前置知识(上指标求和公式)
上指标求和指的是对组合数的上标进行累加求和,常用公式为曲棍球恒等式(朱世杰恒等式):
证明
我们知道组合数有一个递推公式:
再移项:
有:
这玩意显然可以裂项相消:
因为
变种
上指标求和公式有很多变种,这里给出我们下文要用到的:
同样可以裂项证明。
解题思路
25 分
枚举最小值
每个组合可排列成
答案即为:
100 分
考虑容斥,设:
则答案为:
\text{Part 1}
通过前面的推导我们可以得到一个求和式:
我们发现可以分讨来拆掉
情况 1
当
情况 2
当
换元,令
由曲棍球恒等式(不会的看前置知识),得:
因此:
\text{Part 2}
我们同样根据最小值
情况 1
当
情况 2
当
设
每个
情况 3
当
每个
总和为:
换元,令
利用曲棍球恒等式的变种,得:
而
最终答案
代码实现
注意一下边界即可。
::::success[
#include <bits/stdc++.h>
#define i64 long long
using namespace std;
const int N = 2e5 + 5;
const int mod = 1e9 + 7;
vector<i64> fac(N, 0), inv(N, 0);
i64 qpow(i64 base, int b) {
i64 res = 1;
while (b) {
if (b & 1) res = res * base % mod;
base = base * base % mod;
b >>= 1;
}
return res;
}
i64 C(int x, int y) {
if (x - y < 0) return 0;
if (!y || !(x - y)) return 1;
return ((fac[x] * inv[y] % mod) * inv[x - y]) % mod;
}
int in(int l, int r, int x) {
return l <= x && x <= r;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
fac[0] = 1;
for (int i = 1; i < N; i++) {
fac[i] = fac[i - 1] * i % mod;
}
inv[N - 1] = qpow(fac[N - 1], mod - 2);
for (int i = N - 2; i >= 1; i--) {
inv[i] = inv[i + 1] * (i + 1) % mod;
}
int n, m, q;
cin >> n >> m >> q;
while (q--) {
int x, t;
cin >> x >> t;
i64 ans = 0;
for (int i = 1; i <= n; i++) {
if (i == x) continue;
int a = min(n - i - in(i, n, x), t - in(i, i + t, x));
int b = m - 1;
i64 c = fac[m];
ans = (ans + (C(a, b) * c % mod)) % mod;
}
cout << ans << "\n";
}
return 0;
}
::::
::::success[
#include <bits/stdc++.h>
#define i64 long long
using namespace std;
const int N = 2e5 + 5;
const int mod = 1e9 + 7;
vector<i64> fac(N, 0), inv(N, 0);
inline i64 qpow(i64 base, int b) {
i64 res = 1;
while (b) {
if (b & 1) res = res * base % mod;
base = base * base % mod;
b >>= 1;
}
return res;
}
inline i64 C(int x, int y) {
if (x < 0 || y < 0 || x - y < 0) return 0;
if (!y || !(x - y)) return 1;
return ((fac[x] * inv[y] % mod) * inv[x - y]) % mod;
}
inline int in(int l, int r, int x) {
return l <= x && x <= r;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
fac[0] = 1;
for (int i = 1; i < N; i++) {
fac[i] = fac[i - 1] * i % mod;
}
inv[N - 1] = qpow(fac[N - 1], mod - 2);
for (int i = N - 2; i >= 1; i--) {
inv[i] = inv[i + 1] * (i + 1) % mod;
}
int n, m, q;
cin >> n >> m >> q;
while (q--) {
int x, t;
cin >> x >> t;
int A = max(1, x - t), B = x - 1;
i64 S = (((n - t) * C(t, m - 1) % mod) + C(t, m)) % mod;
i64 E1 = C(min(t, n - x), m - 1);
i64 C1 = max(0, min(B, n - t) - A + 1) * C(t - 1, m - 2) % mod;
i64 C2 = 0;
if (max(A, n - t + 1) <= B) {
C2 = (C(min(n - A, t - 1), m - 1) - C(n - x, m - 1) + mod) % mod;
}
i64 ans = ((((S - E1 - C1 - C2) % mod) + mod) % mod) * fac[m] % mod;
cout << ans << "\n";
}
return 0;
}
::::