===================================================================================================================
这道题分析后就可以用高中数学解了,研究生期间也会学组合数学
假设“有趣的数”分别包含 a, b, c, d 个0, 1, 2, 3.
先考虑 (0, 1) 的排列情况,由于0在1前,所以形式是0 0 .. 1 ...1 1。0不能在最高为,故最高位需要使用一个2(即最高位必须是2),剩下 (c + d - 1) 个 (2, 3).
由于 2 必须在3前面。所以把(c + d - 1)个 2 | 3插入 0 1串中,就是高中学的“插空法”。(a + b + 1)个数产生(a + b + 1 + 1)个空。最高位已确定为2,其他2(若有),插在最高位左右一样,所以就是把 (c + d - 1)个数插入(a + b + 1)个空中。
C(a + b + 1 + c + d - 1 - 1, c + d - 1) * (c + d - 1) * ( a + b - 1).
(c + d -1)为 (c + d)个(2, 3)中2或3的个数,(a + b - 1)为 (0, 1)中0或1的个数。
计算这个式子,得计算组合数,我用的快速幂+费马小定理求逆元。也可用 拓展欧几里得。
#include
typedef long long LL;
const int MOD = 1e9 +7, N = 2017;
LL fac[N];
void init() {
fac[0] = fac[1] = 1;
for (int i = 2; i < N; ++i) {
fac[i] = fac[i - 1] * i % MOD;
}
}
LL pow_mod(LL a, LL n) {
LL base = a, res = 1;
while(n) {
if(n & 1) res = res * base % MOD;
base = base * base % MOD;
n >>= 1;
}
return res;
}
LL C(LL n, LL m) {
return fac[n] * pow_mod(fac[n - m], MOD - 2) % MOD * pow_mod(fac[m], MOD - 2) % MOD;
}
int main()
{
//ios::sync_with_stdio(false);
init();
int n;
scanf("%d", &n);
//2 3 最多共有(n-2)个
long long sum = 0;
for (int i23 = 1; i23 <= n - 2; ++i23) {
sum = (sum + C(n - 1, i23 - 1) * (i23 - 1) % MOD * (n - i23 -1) % MOD) % MOD;
}
printf("%lld\n", sum);
return 0;
}