Codeforces 57 C Array ——组合数学

来源:互联网 发布:mysql drop column if 编辑:程序博客网 时间:2024/04/29 02:39

题目链接点击打开链接

题意:一个包含n个元素的数列,元素值只能是1到n中的任意一个数。一个数列优美当且仅当满足以下两个条件之一:1.数列单调不增;2数列单调不减。现在输入一个数n,问符合要求的优美数列有多少个。例如,n==2时,1,1;1,2;2,1;2,2共4个。


思路:输入一个数n,现在有n个位置,每个位置填1到n中的一个数,并且这些数单调不增或单调不减。首先,c(n,k)从n个数中选k个来填入,选定数后,其顺序也就定了。再其次,我们来考虑每一个数的个数。这时,我们回忆高中排列组合时老师讲过的隔板法。k个未知数和为n,求未知数的组合个数,显然c(n-1,k-1)。由乘法原理和加法原理,结果就是

 for(i=1;i<=n;i++)    {        ll tmp;        if(i==1) tmp =  (combine(n,i)*combine(n-1,i-1)*1ll) % mod;        else tmp =  (2*combine(n,i)*combine(n-1,i-1)*1ll) % mod;//i不为1时分递增和递减两种情况        ans  = (ans + tmp) % mod;    }

但是,题目给出的n很大,这样做会超时。所以我们需要对这个求和公式进行化简。改成线性时间复杂度。考虑到c(n-1,k-1)=c(n-1,n-k)。原式变为对c(n,k)*c(n-1,n-k)求和。经过这一转变现在组合数表达的含义已经很清楚。注意到k+n-k = n,n+n-1 = 2n-1。原式的求和可以理解为将2n-1个小球分成两堆,一堆为n,另一堆为n-1。每一次从前一堆中取k个,后一堆中取n-k个。其结果相加,等同于从2n-1个中取n个。因此c(n,k)*c(n-1,n-k)对k从1到n求和,结果就是c(2n-1,n)。最终答案就是2*c(2n-1,n)-n(因为k==1时不×2)。

此外,就是熟练了大组合数取模的板子。

AC代码

#include<iostream>#include<cstdio>#include<cstring>#include<set>#include<cstdio>#include<string>#include<map>#include<algorithm>using namespace std;typedef long long ll;const int inf=1<<28;const int maxn=100005;const ll mod = 1000000007;int dp[2][maxn];ll extend_gcd(ll a,ll b,ll &x,ll &y){    if(b==0)    {        x=1;        y=0;        return a;    }    ll gcd=extend_gcd(b,a%b,x,y);    ll t=x;    x=y;    y=t-a/b*x;    return gcd;}ll Get_Inverse(ll num){    ll x,y;    extend_gcd(num,mod,x,y);    return (x%mod+mod)%mod;}///*************//ll combine(ll n,ll m)//计算组合数C(n,m){    ll t1=1,t2=1;    for(ll i=n;i>m;i--)    {        t1=(t1*i)%mod;        t2=(t2*(i-m))%mod;    }    return t1*Get_Inverse(t2)%mod;}int main(){    int n,i,j;    cin>>n;     ll ans =2*combine(2*n-1,n) - n;    cout<<ans%mod<<endl;    return 0;}


0 0
原创粉丝点击