2017百度之星复赛 1003 Pokémon GO 递推

来源:互联网 发布:如何延长淘宝付款时间 编辑:程序博客网 时间:2024/06/08 06:25

众所周知,度度熊最近沉迷于 Pokémon GO。

今天它决定要抓住所有的精灵球!

为了不让度度熊失望,精灵球已经被事先放置在一个2*N的格子上,每一个格子上都有一个精灵球。度度熊可以选择任意一个格子开始游戏,抓捕格子上的精灵球,然后移动到一个相邻的至少有一个公共点的格子上继续抓捕。例如,(2, 2) 的相邻格子有(1, 1), (2, 1) 和 (1, 2) 等等。

现在度度熊希望知道将所有精灵球都抓到并且步数最少的方案数目。两个方案被认为是不同,当且仅当两个方案至少有一步所在的格子是不同的。

Input

第一行为T,表示输入数据组数。

每组数据包含一个数N。

●1≤T≤100

●1≤N≤10000

Output

对每组数据输出方案数目,结果对 1 000 000 007 取模。

Sample Input
3123
Sample Output
22496


这题显然不是找规律就是递推。数据范围是1e4,多半是O(n^2)的递推预处理然后在处理询问。由题意知要走得步数最小而且每个格子都要至少经过一次,所以如果选择的起点在中间而不是矩形的两侧,则需要走完起点的左半部分后回到这个起点的上方或者下方,再走这个点的右半部分,或者先走右半部分再走左半部分。这就可以运用乘法原理,整体的方案数 =  走完半部分再回来的方案数 *走完半部分的方案数 + 走完半部分再回来的方案数 *走完半部分的方案数 。这样就只要求以矩形的某一侧为起点走完再回到这一侧的方案数,和 不必回来的方案数就可以了。

令 g(n)  =以边界为起点走完矩形再回到边界的方案数 

     φ(n)  =以边界为起点走完矩形的方案数 

      f(n)  =从任意起点走完矩形的方案数

     (以上计入方案数中的各方案都不能走重复的格点)

则f(n)=2*g(n) + {  g(i)* φ( n-i ) + g( n-i +1 )* φ( i-1 )  }{  2<=  i <= n-1 };

   而 g(n) = g(n-1)*2;  ( 因为要回到边界的话就只能先走完左边界右边的所有格点并且回到边界旁边的点,即必须走完右侧2*(n-1)个格点再回到这2*(n-1)个格点的左边界,而且选择上下两个格点作为起点会是不同的方案于是要乘2)

φ(n) = g( n )+ 2* ( 2*φ(n-2) + φ(n-1) ) ( 因为可以回到左边界,所以这种条件下的方案中包括g(n)所计入的方案

而可以选择先向上或者下走完左边界上的2个格点再走右边所以要计入φ(n-1),而另一种情况只能是走完左边界的

2*2格点再走完剩余的2*(n-2)个格点,所以只要再加上2*φ(n-2)就可以了,最后再乘以选择左上或者左下为起点的

选择数就可以得到递推式了。

代码:

#include <cmath>#include <queue>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>#define ll long longusing namespace std;inline void read(int &x){    char ch;    bool flag=false;    for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;    for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());    x=flag?-x:x;}inline void write(int x){    static const int maxlen=100;    static char s[maxlen];        if (x<0) {   putchar('-'); x=-x;}    if(!x){ putchar('0'); return; }    int len=0; for(;x;x/=10) s[len++]=x % 10+'0';    for(int i=len-1;i>=0;--i) putchar(s[i]);}int n;long long g[12000];long long f[12000];long long fai[20000];const long long P=1000000007ll;int main(){    fai[0]=1; g[0]=1;    fai[1]=2; g[1]=2;    f[1]=2;    for (int i=2;i<=10000;i++)        {            g[i]=g[i-1]*2ll%P;            fai[i]= ( g[i]%P+2ll*(2ll*fai[i-2]%P+fai[i-1]%P)%P )%P;        }    for (int i=2;i<=10000;i++)        {            f[i]=2ll*fai[i]%P;            for (int j=2;j<=i-1;j++)                f[i]=( f[i]+g[j]*fai[i-j]%P+g[i-j+1]*fai[j-1]%P )%P;            f[i]=f[i]%P;        }    int T;    read(T);    for (int i=1;i<=T;i++)        {            int x;            read(x);            cout<<f[x]<<endl;        }    return 0;}


原创粉丝点击