CodeForces 57C Array 组合计数+逆元

题目链接:

http://codeforces.com/problemset/problem/57/C

题意:

给你一个数n,表示有n个数的序列,每个数范围为[1,n],叫你求所有非降和非升序列的个数。

题解:

由于对称性,我们只要求非降序的个数就可以了(n个数全部相等的情况既属于非升也属于非降)

我们在满足条件的n个数之前加一个虚节点1,在第n个数之后加一个虚节点n,那么考虑这n+2个数组成的非降序列:

假设序列里的第i个数为a[i],我们设xi=a[i+1]-a[i]+1,1<=i<=n+1,则满足每个数>=1,且sum(x[1],x[2]...x[n+1])=2*n;

那么相当于求将2*n分成n个部分,且每个部分的值大于等于1,则易得非降序列总数为:C(n,2*n-1)(2*n-1 选 n)

所以最后的答案是2*C(n,2*n-1)-n;

代码:

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 using namespace std;
 5 
 6 const int mod = 1000000007;
 7 typedef long long LL;
 8 int n;
 9 //扩展欧几里得
10 void gcd(int a, int b, int &d, int &x, int &y) {
11     if (!b) { d = a; x = 1; y = 0; }
12     else { gcd(b, a%b, d, y, x); y -= x*(a / b); }
13 }
14 //求逆元
15 int inv(int a) {
16     int d, x, y;
17     gcd(a, mod, d, x, y);
18     return x;
19 }
20 //求阶乘
21 int solve(int _n,int x) {
22     LL ret = 1;
23     while (_n--) {
24         ret *= x;
25         ret %= mod;
26         x--;
27     }
28     return ret;
29 }
30 
31 int main() {
32     while (scanf("%d", &n) == 1 && n) {
33         int ans = (LL)solve(n, 2 * n - 1)*inv(solve(n,n))%mod;
34         ans = ans * 2 - n;
35         ans = (ans%mod + mod) % mod;
36         printf("%d
", ans);
37     }
38     return 0;
39 }
原文地址:https://www.cnblogs.com/fenice/p/5460389.html