有劳各位大佬帮忙看一下我这题哪里错了??
查看原帖
有劳各位大佬帮忙看一下我这题哪里错了??
291976
quanjun楼主2022/12/22 14:33

这里我用 CnmC_{n}^{m} 表示”从 nn 个不同的数里选出 mm 个数的组合方案数”。

首先考虑 x>yx \gt y 的情况,此时我们只需要执行:

  • in+1ii \leftarrow n+1-i
  • jn+1jj \leftarrow n+1-j
  • x,yy,xx, y \leftarrow y,x(即交换 xxyy 的值)

就能将问题转成 x<yx \lt y 的情况了。

所以下面就只需要针对 x<yx \lt y 的情况做展开。

有两种情况需要分开来讨论:

  • 情况1:y=ny = n
  • 情况2:y<ny \lt n

情况1:y=ny = n

此时情况如下:

首先可以确定的是 ai=x,aj=y=na_i = x, a_j = y = n,所以区间 [1,j][1, j] 是递增的,区间 [j,n][j,n] 是递减的。

可以把区间 [1,n][1, n] 划分成三部分:

  • 11 部分:区间 [1,i1][1, i-1] (即 aia_i 左边那些数)一共有 i1i-1 个位置
  • 22 部分:区间 [i+1,j1][i+1, j-1] (即 aia_iaja_j 中间那些数)一共有 ji1j-i-1 个位置
  • 33 部分:区间 [j+1,n][j+1, n](即 aja_j 右边那些数)一共有 njn-j 个位置

首先考虑第 22 部分,因为从 aia_iaja_j 是递增的,所以这一部分能够选的数的范围是 [x+1,n1][x+1, n-1],所以这一部分我们要从 [x+1,n1][x+1, n-1]nx1n-x-1 个数里面选 ji1j-i-1 个数,对应的方案数为 Cnx1ji1C_{n-x-1}^{j-i-1}

然后再考虑第 11 部分,这一部分的数都应该 <x\lt x,可以选的数有 x1x-1 个,需要选择 i1i-1 个数,对应的方案数为 Cx1i1C_{x-1}^{i-1}

1122 部分确定好之后,第 33 部分自然就确定了。

所以,当 y=ny = n 时,总的方案数为:

Cnx1ji1Cx1i1C_{n-x-1}^{j-i-1} \cdot C_{x-1}^{i-1}

当然,若 nx1<ji1n-x-1 \lt j-i-1 或者 x1<i1x-1 \lt i-1,则方案数为 00

情况2:y<ny \lt n

此时又要分两种情况讨论,设最大值为 aka_k,则 kk 有两种情况:

  • 情况(1): i<k<ji \lt k \lt j(可以从 i+1i+1j1j-1 枚举 kk
  • 情况(2): j<k<nj \lt k \lt n(可以从 j+1j+1n1n-1 枚举 kk

情况(1) i<k<ji \lt k \lt j

此时情况如下:

可以把区间 [1,n][1, n] 划分成四部分:

  • 11 部分:区间 [1,i1][1,i-1](即 aia_i 左边那些数)一共有 i1i-1 个位置
  • 22 部分:区间 [i+1,k1][i+1, k-1](即 aia_iaka_k 中间那些数)一共有 ki1k-i-1 个位置
  • 33 部分:区间 [k+1,j1][k+1, j-1](即 aka_kaja_j 中间那些数)一共有 jk1j-k-1 个位置
  • 44 部分:区间 [j+1,n][j+1, n](即 aja_j 右边那些数)一共有 njn-j 个位置

先考虑第 33 部分,这一部分需要从 [y+1,n1][y+1, n-1]ny1n-y-1 个数中选出 jk1j-k-1 个数,对应的方案数为 Cny1jk1C_{n-y-1}^{j-k-1}

然后再考虑第 22 部分,这一部分需要所有 >x\gt x 的数,但是区间 [k,j][k, j] 已经占去了 jk+1j-k+1 个数字(其中区间 [k+1,j1][k+1,j-1] 占去了 jk1j-k-1 个,aia_iaja_j 各占去了一个),剩下可选的 >x\gt x 的数还有 (nx)(jk+1)=nxj+k1(n - x) - (j-k+1) = n-x-j+k-1 个数,需要从这些数里选出恰好 ki1k-i-1 个数放在区间 [i+1,k1][i+1, k-1] 中,对应的方案数为 Cnxj+k1ki1C_{n-x-j+k-1}^{k-i-1}

然后再考虑第 11 部分,这一部分和第 2233 部分是没有冲突的,可以选的数有 x1x-1 个,需要选择 i1i-1 个数,对应的方案数为 Cx1i1C_{x-1}^{i-1}

对于任意一个 i<k<ji \lt k \lt j,对应的方案数是:

Cny1jk1Cnxj+k1ki1Cx1i1C_{n-y-1}^{j-k-1} \cdot C_{n-x-j+k-1}^{k-i-1} \cdot C_{x-1}^{i-1}

总方案数为:

k=i+1j1Cny1jk1Cnxj+k1ki1Cx1i1\sum\limits_{k=i+1}^{j-1} C_{n-y-1}^{j-k-1} \cdot C_{n-x-j+k-1}^{k-i-1} \cdot C_{x-1}^{i-1}

情况(2): j<k<nj \lt k \lt n

此时情况如下:

可以把区间 [1,n][1, n] 划分成四部分:

  • 11 部分:区间 [1,i1][1, i-1](即 aia_i 左边那些数) 一共有 i1i-1 个位置
  • 22 部分:区间 [i+1,j1][i+1, j-1](即 aia_iaja_j 中间那些数) 一共有 ji1j-i-1 个位置
  • 33 部分:区间 [j+1,k1][j+1, k-1](即 aja_jaka_k 中间那些数) 一共有 kj1k-j-1 个位置
  • 44 部分:区间 [k+1,n][k+1, n](即 aka_k 右边那些数) 一共有 nkn-k 个位置

然后我们会发现,第 112233 部分所选择的数的范围是没有重叠的,所以:第一部分选数的方案数为 Cx1i1C_{x-1}^{i-1};第二部分选数的方案数为 Cyx1ji1C_{y-x-1}^{j-i-1};第三部分选数的方案数为 Cny1kj1C_{n-y-1}^{k-j-1},前 33 部分确定下来了,第四部分也就确定了。

对于任意一个 j<k<nj \lt k \lt n,对应的方案数是:

Cx1i1Cyx1ji1Cny1kj1C_{x-1}^{i-1} \cdot C_{y-x-1}^{j-i-1} \cdot C_{n-y-1}^{k-j-1}

总方案数为:

k=i+1n1Cx1i1Cyx1ji1Cny1kj1\sum\limits_{k=i+1}^{n-1} C_{x-1}^{i-1} \cdot C_{y-x-1}^{j-i-1} \cdot C_{n-y-1}^{k-j-1}

结合两部分的情况,总的方案数是:

k=i+1j1Cny1jk1Cnxj+k1ki1Cx1i1+k=i+1n1Cx1i1Cyx1ji1Cny1kj1\sum\limits_{k=i+1}^{j-1} C_{n-y-1}^{j-k-1} \cdot C_{n-x-j+k-1}^{k-i-1} \cdot C_{x-1}^{i-1} + \sum\limits_{k=i+1}^{n-1} C_{x-1}^{i-1} \cdot C_{y-x-1}^{j-i-1} \cdot C_{n-y-1}^{k-j-1}

然后代码实现的时候会有些情况的组合数不合法,不合法的组合数均视为 00 即可。

下面是我的代码:

#include <bits/stdc++.h>
using namespace std;
const long long MOD = 1e9 + 7;
const int maxn = 110;
long long c[maxn][maxn];

void init() {
    c[0][0] = 1;
    for (int i = 1; i < maxn; i++) {
        for (int j = 0; j <= i; j++) {
            if (!j || j==i) c[i][j] = 1;
            else c[i][j] = (c[i-1][j-1] + c[i-1][j]) % MOD;
        }
    }
}

long long cal(int n, int i, int j, int x, int y) {
    if (x > y) {    // 如果 x > y,调整一下 i 和 j 的位置,这样只用处理 x < y 的两种情况就可以了
        i = n + 1 - i;
        j = n + 1 - j;
        swap(x, y);
    }
    if (y == n) {   // y == n 的情况特判
        return c[n-x-1][j-i-1] * c[x-1][i-1] % MOD;
    }
    // 接下来分析 x < y < n 的情况
    long long sum = 0;
    for (int k = i+1; k < j; k++)
        sum = (sum + c[n-y-1][j-k-1] * c[n-x-j+k-1][k-i-1] % MOD * c[x-1][i-1] % MOD) % MOD;
    for (int k = j+1; k < n; k++)
        sum = (sum + c[x-1][i-1] * c[y-x-1][j-i-1] % MOD * c[n-y-1][k-j-1] % MOD) % MOD;
    return sum;
}

int T, n, i, j, x, y;

int main() {
    init();
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d%d%d%d", &n, &i, &j, &x, &y);
        printf("%lld\n", cal(n, i, j, x, y));
    }
    return 0;
}

但是样例的最后一组就出错了,样例输出是 4788,我的输出是 7308

请问我是哪里考虑错了吗?

2022/12/22 14:33
加载中...