长度为n的队列,每轮出队队头,再在剩下k−1人中选m−1人出队,问出队结果集大小。
组合数,快速幂
无论上一轮怎么出队,当前这一轮的结果集大小都不会变,因此我们可以利用递推;
注意一下剩余人数可能不足k人的变化,设fn表示m,k,确定的情况下,n个人的出队结果集,则简单的可以写成:
fn=Cmin(n,k)−1m−1∗fn−m注意到这个递推显然是可以分成两部分的,取决于min(n,k);
因为n每次减少m,那么min(n,k)取k的次数x可以直接计算得到:
n−xm<=kx>=mn−kxmin=⌈mn−k⌉那么式子变成
fn=[Ck−1m−1]x∗fn−xm乘号左边快速幂算一下O(logn);右边已知了n−xm<=k,则暴力计算或预处理O(k)。
预处理O(k2),每个Case为O(k),共T个。
总的来说:O(k2)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5010, mod = 1e9 + 7;
int n, m, k;
int c[N][N];
int qmi(int a, int k) {
int res = 1;
while (k) {
if (k & 1) res = res * a % mod;
k >>= 1;
a = a * a % mod;
}
return res;
}
void init() {
for (int i = 0; i < N; i++)
for (int j = 0; j <= i; j++)
if (!j) c[i][j] = 1;
else c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
init();
int T_T = 1;
cin >> T_T;
while (T_T--) {
cin >> n >> m >> k;
int x = n > k ? (n - k + m - 1) / m : 0; // x不能负
int ans = qmi(c[k - 1][m - 1], x);
n -= x * m;
while (n > m) {
ans = ans * c[n - 1][m - 1] % mod;
n -= m;
}
cout << ans << "\n";
}
return 0;
}
fn−xm能不能算快点?