小白月赛17 J-计数 组合计数

传送门:https://ac.nowcoder.com/acm/contest/1085/J

题意

有一个含有n个数字的序列,每个数的大小是不超过1000的正整数,同时这个序列是个单调不增序列。但是很不幸的是,序列在保存过程中有些数字丢失了,请你根据上述条件,计算出有多少种不同的序列满足上述条件,答案对1000000007取模。

思路

方法一:隔板法

假如连续一段有n个0,所以需要在这n个位置中插入数,而插入的数为[1,m](大家应该能看出来)。 所以我们假设[1,m] 中1出现$a_1$次,m出现$a_m$次。即i出现$a_i$次。 所以要将这些数字插入n个位置中,即统计他们可以出现的次数,即

$$n=a_1+a_2+…+a_m(0\leq a_i \leq n)$$

等价于n个小球放进m个盒子里,隔板法即可,由于$a_i$可以等于0,所以我们让每一个盒子都是事先放进1个小球,即n+m,这样求出的方案为$C_{n+m-1}^{m-1}$,每一种方案为一种序列。

Code(159MS)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
##include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const ll mod = 1e9 + 7;
const int N = 1e6 + 10;
ll fac[N];
ll inv[N];

ll quick_pow(ll a, ll b) {
ll ans = 1;
while(b) {
if(b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}

void init() {
fac[0] = 1;
for(int i = 1;i < N; i++) fac[i] = fac[i - 1] * i % mod;
inv[N - 1] = quick_pow(fac[N - 1], mod - 2);
for(int i = N - 2;i >= 0; i--) {
inv[i] = inv[i + 1] * (i + 1) % mod;
}
}

ll C(int m, int n) {
ll ans = fac[m] * inv[n] % mod * inv[m - n] % mod;
return ans;
}

int main() {
init();
int n;
cin >> n;
int pre = 1000;
int cnt = 0;
ll ans = 1;
for(int i = 1;i <= n; i++) {
int x;
cin >> x;
if(!x) cnt++;
else {
int len = pre - x + 1;
ans = ans * C(cnt + len - 1, len - 1) % mod;
pre = x;
cnt = 0;
}
}
if(cnt) {
ans = ans * C(cnt + pre - 1, pre - 1) % mod;
}
cout << ans << endl;
}

方法二:单调不增变单调递减

$将单调不增序列变为点掉递减序列,只需将i位置上的数+(n-i+1)即可。$

$对于一段连续为0的区间,就不需要考虑会取同样的值,直接在可以选择的数中选出n个即可。$

$假设可以取的值有m个,那么方案数为C_{m}^n$

$注意0和n位置也要处理。$

Code(159MS)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
##include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const ll mod = 1e9 + 7;
const int N = 1e6 + 10;
ll fac[N];
ll inv[N];

ll quick_pow(ll a, ll b) {
ll ans = 1;
while(b) {
if(b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}

void init() {
fac[0] = 1;
for(int i = 1;i < N; i++) fac[i] = fac[i - 1] * i % mod;
inv[N - 1] = quick_pow(fac[N - 1], mod - 2);
for(int i = N - 2;i >= 0; i--) {
inv[i] = inv[i + 1] * (i + 1) % mod;
}
}

ll C(int m, int n) {
ll ans = fac[m] * inv[n] % mod * inv[m - n] % mod;
return ans;
}

int a[N];

int main() {
init();
int n;
cin >> n;
n += 2;
ll ans = 1;
a[1] = 1000 + n; a[n] = 2;
for(int i = 2;i < n; i++) {
cin >> a[i];
if(a[i])
a[i] += n - i + 1;
}
int suf = 0;
for(int i = 1;i <= n; i++) {
if(a[i]) {
if(suf)
ans = ans * C(a[suf] - a[i] - 1, i - suf - 1) % mod;
suf = i;
}
}
cout << ans << endl;
}

本文作者:jujimeizuo
本文地址https://blog.jujimeizuo.cn/2020/11/19/nowcoder-newbie17-j/
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0 协议。转载请注明出处!