HDU5673 Robot DP 默慈金数 线性求1~n逆元 bestcoder

Robot


Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 141    Accepted Submission(s): 61




Problem Description
There is a robot on the origin point of an axis.Every second, the robot can move right one unit length or do nothing.If the robot is 
on the right of origin point,it can also move left one unit length.A route is a series of movement. How many different routes there are
that after n seconds the robot is still located on the origin point?
The answer may be large. Please output the answer modulo 1,000,000,007
 


Input
There are multiple test cases. The first line of input contains an integer T(1≤T≤100) indicating the number of test cases. For each test case:


The only line contains one integer n(1≤n≤1,000,000).
 


Output
For each test case, output one integer.
 


Sample Input
3
1
2
4
 


Sample Output
1
2
9
 


Source
BestCoder Round #81 (div.2)
 



题意:一个机器人从一个线段左原点可以左右移动(在原点的时候不能往左走),一秒可以动一格或者不动,n秒之后又回到原点,问一共可能的路径有多少,比如n=4的时候(0,0,1,-1)和(1,-1,0,0)是两种路径。最后结果要mod一个1e9+7,因为结果可能很大

思路:这个题可以用一种DP的思路来解决,题解直接说是默慈金数了,没说是怎么来的。其实我们用dp[i]代表n=i时的答案,dp[i]可以由前面的更新来。

首先dp[i]分为两部分:1.当第一步是原地不动的时候。2.第一步向右走。

对于1:=dp[i-1]。

对于2:此时可以枚举与第一步对应的回到原点的-1(向左走)的位置,-1一共有i-1个位置可选。

比如当i=5时,就可以分为

dp[5]=

(0,X,X,X,X)dp[4]+

(1,-1,X,X,X)dp[0]*dp[3]+

(1,X,-1,X,X)dp[1]*dp[2]+

(1,X,X,-1,X)dp[2]*dp[1]+

(1,X,X,X,-1)dp[3]*dp[0]

当时没做出来,因为觉得这几种情况有交集,但实际上仔细想过,是互斥的,枚举的-1都是与第一个1对应的回到原点的时候,所以左右两部分分别都是均衡的,向右走和向左走的步数是一样的。

所以左边的公式就是这么来的。但是怎么推到右边的那个通项公式。。。。我也不知道。。。盗了一张百度的图,这玩意其实就是默慈金数,记住就好了,用到的时候拿出来用

最后结果就是

dp[i]=((dp[i-1]*(2*(i-1)+3)+3*(i-1)*dp[i-2])%mod/(i-1+3))%mod;

然后问题又来了,有个讨厌的分母,在mod的时候只能用逆元给去掉,

这里要求1、2、3……一直到n的关于mod的逆元,这里有一种线性的方法,

m[1]=1;
for(int i=2;i<=1000009;i++){
    m[i]=((mod-mod/i)*m[mod%i])%mod;
}

这么逆天的方法当然不是我想的,转自:http://blog.miskcoo.com/2014/09/linear-find-all-invert

乘法逆元相关知识传送门:http://www.cnblogs.com/tiankonguse/archive/2012/08/14/2638949.html

所以最后公式就成了

dp[i]=((dp[i-1]*(2*(i-1)+3)+3*(i-1)*dp[i-2])%mod*m[i-1+3])%mod;

打个表,结束。>_<





#include <iostream>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <algorithm>
#include <vector>
#include <queue>
#include <iomanip>
#include <time.h>
#include <set>
#include <map>
#include <stack>
using namespace std;
typedef long long LL;
const int INF=0x7fffffff;
const int MAX_N=10000;
long long mod=1000000007;
int T;
long long n;
long long dp[1000009];
long long m[1000009];//i关于mod的逆元
int main(){

    m[1]=1;
    for(int i=2;i<=1000009;i++){
        m[i]=((mod-mod/i)*m[mod%i])%mod;
    }
    dp[1]=1;
    dp[2]=2;
    dp[3]=4;
    for(long long i=4;i<=1000000;i++){
        dp[i]=((dp[i-1]*(2*(i-1)+3)+3*(i-1)*dp[i-2])%mod*m[i-1+3])%mod;
    }
    cin>>T;
    while(T--){
        cin>>n;
        cout<<dp[n]<<endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值