题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5673
分析:
这道题是一道裸的默慈金数,比较容易想到的是用卡特兰数来做。不了解的可以先学习一下。
卡特兰数:http://www.cnblogs.com/yaoyueduzhen/p/5456490.html
默慈金数:http://www.cnblogs.com/yaoyueduzhen/p/5456530.html
记路径长度为nn,那么机器人最多向右走⌊n/2⌋步并向左走⌊n/2⌋步。
Ans(n)=∑C(n,2i) Catalan(i)
其中 C(n,2i) 表示从n个物品中取2*i个的组合数,Catalan(n)表示第n个卡特兰数,0 <= i <= ⌊n/2⌋
基于n的取值范围,此题可以预处理出1,000,001以内的乘法逆元、卡特兰数。
每次询问,都可以递推组合数,或者提前一次性预处理好阶乘和阶乘的逆元得到组合数;累加组合数与相应卡特兰数的乘积,得到答案。
事实上,Ans(n)是第n个默慈金数,利用递推公式可以快速求出。
卡特兰数代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring> using namespace std;
const long long mod=;
long long n;
long long ans[],ni[]; long long power(long long a,long long n,long long m)
{
long long ans=,tmp=a;
while(n)
{
if(n&)
ans=ans*tmp%m;
tmp=tmp*tmp%m;
n=n>>;
}
return ans%m;
} void init()
{
ans[]=;
ans[]=;
for(long long i=;i<=;i++)
ni[i]=power(i,mod-,mod)%mod;
for(long long i=;i<=;i++)
ans[i]=(*i-)%mod*ans[i-]%mod*ni[i+]%mod;
} int main()
{
init();
int T;
scanf("%d",&T);
while(T--)
{
scanf("%lld",&n);
long long res=,tmp=;
for(long long i=;i*<=n;i++)
{
res=(res+tmp*ans[i]%mod)%mod;
tmp=tmp*(n-*i)%mod*(n-*i-)%mod*ni[*i+]%mod*ni[*i+]%mod;
}
cout<<res<<endl;
}
return ;
}
默慈金数代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring> using namespace std; const long long mod=;
long long n;
long long ans[];
long long power(long long a,long long n,long long m)
{
long long ans=,tmp=a;
while(n)
{
if(n&)
ans=ans*tmp%m;
tmp=tmp*tmp%m;
n=n>>;
}
return ans;
} int main()
{
ans[]=;
ans[]=;
for(long long i=;i<=;i++)
ans[i+]=((*i+)%mod*ans[i]%mod+*i*ans[i-]%mod)%mod*power(i+,mod-,mod)%mod;
int T;
scanf("%d",&T);
while(T--)
{
scanf("%lld",&n);
cout<<ans[n]<<endl;
}
return ;
}