这题显然不是找规律就是递推。数据范围是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 long
using 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;
}