問題の説明#
a[1]=a[2]=a[3]=1
a[x]=a[x-3]+a[x-1] (x>3)
a 数列の第 n 項を 1000000007(10^9+7)で割った余りを求めよ。
入出力の形式#
入力形式:#
最初の行には整数 T が 1 つ与えられる。これは問い合わせの数を表す。
以下の T 行には、各行に正整数 n が 1 つずつ与えられる。
出力形式:#
各行に、答えを表す非負整数を出力する。
入出力の例#
入力例 #1:
3
6
8
10
出力例 #1:
4
9
19
説明#
30% のデータに対して、n<=100;
60% のデータに対して、n<=2*10^7;
100% のデータに対して、T<=100,n<=2*10^9;
解決#
タイトルの通り、テンプレート問題
[f[x],f[x-1],f[x-2],f[x-3]]=[f[x-1],f[x-2],f[x-3],f[x-4]]*A
A=[
1 1 0 0
0 0 1 0
1 0 0 1
0 0 0 0
]
思いつかないかもしれないところ(私が思いつかなかったところ):
- a [x]=a [x-3]+a [x-1] ,しかし x-2,x-4 も行列に入れることができる
- n<=3 の場合、無限ループになるので特別に処理する
#include<iostream>
#include<cstring>
#define min(a,b) ((a)>(b)?(b):(a))
#define max(a,b) ((a)<(b)?(b):(a))
using namespace std;
const long long MAXN=10,mo=1e9+7;
struct juzhen{
public:
juzhen(int han,int lin){
h=han,l=lin;
memset(v,0,sizeof(v));
}
juzhen(){
memset(v,0,sizeof(v));
}
void cleanForPow(void){
memset(v,0,sizeof(v));
int p=min(h,l);
for(int i=1;i<=p;++i){
v[i][i]=1;
}
}
friend juzhen operator *(juzhen a,juzhen b){
juzhen c(a.h,b.l);
if(a.l!=b.h)return c;
for(int i=1;i<=a.h;++i){
for(int j=1;j<=b.l;++j){
long long s=0;
for(int k=1;k<=a.l;++k){
s=s%mo+(a.v[i][k]*b.v[k][j]%mo);
}
c.v[i][j]=s;
}
}
return c;
}
juzhen pow(int k){
juzhen res=*this;
juzhen ret(h,l);
ret.cleanForPow();
while(k){
if(k&1){
ret=ret*res;
}
res=res*res;
k>>=1;
}
return ret;
}
void setV(long long t[MAXN][MAXN]){
for(int i=1;i<=h;++i){
for(int j=1;j<=l;++j){
v[i][j]=t[i][j];
}
}
}
long long v[MAXN][MAXN];
private:
int h,l;
};
int main(void){
int T;
cin>>T;
long long tmp[MAXN][MAXN]={{0},{0,2,1,1,1}};
long long tmp2[MAXN][MAXN]={
{0},
{0,1,1,0,0},
{0,0,0,1,0},
{0,1,0,0,1},
{0,0,0,0,0}
};
juzhen a(1,4);
juzhen b(4,4);
a.setV(tmp);
b.setV(tmp2);
while(T--){
int n;
cin>>n;
if(n<=3)cout<<"1\n",continue;
juzhen ans=a*b.pow(n-4);
cout<<ans.v[1][1]<<'\n';
}
return 0;
}