U448964 组队规则
  • 板块题目总版
  • 楼主hhl666
  • 当前回复2
  • 已保存回复2
  • 发布时间2024/7/15 18:23
  • 上次更新2024/7/15 18:32:14
查看原帖
U448964 组队规则
648297
hhl666楼主2024/7/15 18:23

题意:

长度为nn的队列,每轮出队队头,再在剩下k−1k-1人中选m−1m-1人出队,问出队结果集大小。

前置知识:

组合数,快速幂

分析:

无论上一轮怎么出队,当前这一轮的结果集大小都不会变,因此我们可以利用递推;

注意一下剩余人数可能不足kk人的变化,设fnf_n表示mm,kk,确定的情况下,nn个人的出队结果集,则简单的可以写成:

fn=Cmin(n,k)−1m−1∗fn−mf_n = C_{min(n, k)-1}^{m-1}*f_{n-m}

注意到这个递推显然是可以分成两部分的,取决于min(n,k)min(n,k);

因为nn每次减少mm,那么min(n,k)min(n,k)取kk的次数xx可以直接计算得到:

n−xm<=kx>=n−kmxmin=⌈n−km⌉n-xm<=k \\ x>=\frac{n-k}{m} \\ x_{min}=\lceil\frac{n-k}{m}\rceil

那么式子变成

fn=[Ck−1m−1]x∗fn−xmf_n = [C_{k-1}^{m-1}]^{x}*f_{n-xm}

乘号左边快速幂算一下O(logn)O(logn);右边已知了n−xm<=kn-xm<=k,则暴力计算或预处理O(k)O(k)。

时间复杂度:

预处理O(k2)O(k^2),每个CaseCase为O(k)O(k),共TT个。

总的来说:O(k2)O(k^2)

参考代码:

#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−xmf_{n-xm}能不能算快点?

2024/7/15 18:23
加载中...