解题思路
好水的紫题,感觉是黄 or 绿。
在以下的推导中,另 l l l 为目前区间长度, k k k 为目前区间中已点亮的灯的个数。
我们分三类讨论。
-
对于正常情况,选择灯的方法有 C l k C_l^k Clk 种,对于每种方法都可以从区间中选出若干个 k ′ = 0 k'=0 k′=0 的区间( k ′ = l k'=l k′=l 的区间不需要处理)用 第二类的方法处理。
-
对于 k = 0 k=0 k=0 的情况,我们只能从两端向中间点,每次可以点左边也可以点右边,中心点不需要选择,所有共有 2 l − 1 2^{l-1} 2l−1 种方法。
-
对于区间一段是端点的情况,我们只能从有亮灯的地方往另一端点, 1 1 1 种。
代码实现
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1005, P = 1e9 + 7;
ll a[N], C[N][N];
void init_C() {
for (int i = 0; i < N; ++i) {
for (int j = 0; j <= i; ++j) {
if (i == 0 || j == 0 || i == j) {
C[i][j] = 1;
} else {
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % P;
}
}
}
}
ll qpow(ll x, ll y) {
ll res = 1;
while (y) {
if (y & 1) {
res = res * x % P;
}
x = x * x % P;
y >>= 1;
}
return res;
}
int main() {
ios :: sync_with_stdio(0);
init_C();
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
cin >> a[i];
}
sort(a + 1, a + m + 1);
ll res = 1;
int k = n - m;
for (int i = 1; i < m; ++i) {
int l = a[i + 1] - a[i] - 1;
if (!l) {
continue;
}
res = res * C[k][l] % P * qpow(2, l - 1) % P;
k -= l;
}
res = res * C[k][a[1] - 1] % P;
cout << res << '\n';
return 0;
}
Good Good 贺题, Day Day Up !!