BuringStraw

BuringStraw

【テンプレート】行列加速(数列)

問題の説明#

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;
}
読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。