本題算是簽到題,但因爲賽中花費了過多的時間去滴吧格,形成了沒必要要的浪費以及智商掉線,因此有必要記錄一下坑點node
題意:方格從1到n,每一格mjl能夠選擇吃魚/巧克力/雞腿,求走到n格時知足
1.每三格不可重複同一種食物
2.每三格均不一樣食物時中間格子不可吃巧克力
3.每三格先後兩格不可同時吃巧克力
以上三個條件的方案數,n<1e10c++
太長不看版:打表+快速冪ACspa
長篇吐槽版code
很顯然的,設\(dp[n][i][j][k]\),走到第\(n\)格時第\(n-2\)格的食物是\(i\),第\(n-1\)的食物是\(j\),第\(n\)的食物是\(k\)的方案數
而後一波轉移套上矩陣快速冪,應該沒問題?get
那麼首先應該搞出全部轉移,emmm好像方程不少,手寫花了必定時間發現寫歪了(傻逼作法1
因而寫了個程序暴力轉移it
rep(i,1,3)rep(j,1,3)rep(k,1,3){ printf("dp[i][%d][%d][%d]=",i,j,k); bool flag=0; if(i==j&&j==k&&i==k) flag=1; if(j==2&&i!=2&&k!=2&&i!=k) flag=1; if(i==2&&k==2) flag=1; if(flag){ cout<<0<<endl; continue; } flag=0; int jj=i,kk=j; rep(ii,1,3){ if(ii==jj&&jj==kk&&ii==kk)continue; if(jj==2&&ii!=2&&kk!=2&&ii!=kk)continue; if(ii==2&&kk==2)continue; printf("dp[i-1][%d][%d][%d]+",ii,jj,kk); flag=1; } cout<<endl; }
因而獲得class
dp[i][1][1][1]=0 dp[i][1][1][2]=dp[i-1][2][1][1]+dp[i-1][3][1][1]; dp[i][1][1][3]=dp[i-1][2][1][1]+dp[i-1][3][1][1]; dp[i][1][2][1]=dp[i-1][1][1][2]+dp[i-1][3][1][2]; dp[i][1][2][2]=dp[i-1][1][1][2]+dp[i-1][3][1][2]; dp[i][1][2][3]=0; dp[i][1][3][1]=dp[i-1][1][1][3]+dp[i-1][2][1][3]+dp[i-1][3][1][3]; dp[i][1][3][2]=dp[i-1][1][1][3]+dp[i-1][2][1][3]+dp[i-1][3][1][3]; dp[i][1][3][3]=dp[i-1][1][1][3]+dp[i-1][2][1][3]+dp[i-1][3][1][3]; dp[i][2][1][1]=dp[i-1][1][2][1]+dp[i-1][2][2][1]; dp[i][2][1][2]=0; dp[i][2][1][3]=dp[i-1][1][2][1]+dp[i-1][2][2][1]; dp[i][2][2][1]=dp[i-1][1][2][2]+dp[i-1][3][2][2]; dp[i][2][2][2]=0; dp[i][2][2][3]=dp[i-1][1][2][2]+dp[i-1][3][2][2]; dp[i][2][3][1]=dp[i-1][2][2][3]+dp[i-1][3][2][3]; dp[i][2][3][2]=0; dp[i][2][3][3]=dp[i-1][2][2][3]+dp[i-1][3][2][3]; dp[i][3][1][1]=dp[i-1][1][3][1]+dp[i-1][2][3][1]+dp[i-1][3][3][1]; dp[i][3][1][2]=dp[i-1][1][3][1]+dp[i-1][2][3][1]+dp[i-1][3][3][1]; dp[i][3][1][3]=dp[i-1][1][3][1]+dp[i-1][2][3][1]+dp[i-1][3][3][1]; dp[i][3][2][1]=0; dp[i][3][2][2]=dp[i-1][1][3][2]+dp[i-1][3][3][2]; dp[i][3][2][3]=dp[i-1][1][3][2]+dp[i-1][3][3][2]; dp[i][3][3][1]=dp[i-1][1][3][3]+dp[i-1][2][3][3]; dp[i][3][3][2]=dp[i-1][1][3][3]+dp[i-1][2][3][3]; dp[i][3][3][3]=0;
先試試樣例,臥槽怎麼全是0的
發現忘了前3項特判初始化(傻逼作法2
趕忙加上去程序
rep(i,1,3)dp[1][0][0][i]=dp[0][0][0][0]; rep(i,1,3)rep(j,1,3)dp[2][0][i][j]+=dp[1][0][0][i]; rep(i,1,3)rep(j,1,3)rep(k,1,3){ if(i==j&&j==k&&i==k)continue; if(j==2&&i!=2&&k!=2&&i!=k)continue; if(i==2&&k==2)continue; dp[3][i][j][k]+=dp[2][0][i][j]; }
搞完後怒上27*27的矩陣,然而TLE(傻逼作法3時間
再套了個費馬小定理,TLE(hhh比賽
dalao指點了不如試試BM?沒辦法只好上了
獲得f[n]=2f[n-1]-f[n-2]+3f[n-3]+2f[n-4]
再怒上一波快速冪,發現答案歪了..掛機10min
最後偶然發現必須後幾項開始推纔是正確的(傻逼作法4
推完後改代碼發現又遞推不上去了,兩我的同時寫都歪了
再次發現遞推矩陣的上三角寫歪了一位...(傻逼作法5
對了,挪板子的時候發現MOD寫成了1e9+9,幸虧被隊友發現了hhh(傻逼作法6
最後奉上代碼
#include<bits/stdc++.h> #define rep(i,j,k) for(int i=j;i<=k;i++) #define rrep(i,j,k) for(int i=j;i>=k;i--) #define println(a) printf("%lld\n",(ll)(a)) #define printbk(a) printf("%lld ",(ll)(a)) using namespace std; typedef long long ll; const int maxn = 100; const int MAXN = maxn; const ll MOD = 1e9+7; inline ll mod(ll a){return a%MOD;} ll read(){ ll x=0,f=1;register char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } ll b[5][5]={ {0,0,0,0,0}, {0,2,-1,3,2}, {0,1,0,0,0}, {0,0,1,0,0}, {0,0,0,1,0}, }; ll c[]={0,2956,1286,560,244}; struct Mat{ ll m[5][5],r,c; void node(int rr,int cc,bool unit=0){ r=rr;c=cc; memset(m,0,sizeof m); if(unit) rep(i,1,4) m[i][i]=1; } void print(){ rep(i,1,4){ rep(j,1,4){ cout<<m[i][j]<<" "; } cout<<endl; } } }; Mat operator * (Mat a,Mat b){ Mat ans;ans.node(a.r,b.c); rep(i,1,4){ rep(j,1,4){ int t=4; rep(k,1,t){ ans.m[i][j]+=mod(a.m[i][k]*b.m[k][j]); ans.m[i][j]=mod(ans.m[i][j]); } } } return ans; } Mat qmod(Mat a,ll n){ Mat ans;ans.node(4,4,1); while(n){ if(n&1) ans=ans*a; n>>=1; a=a*a; } return ans; } ll ans[]={0,3,9,20,46,106,244,560,1286,2956}; int main(){ int T=read(); while(T--){ Mat base,base2; base.node(4,4); base2.node(4,4); rep(i,1,4) rep(j,1,4) base.m[i][j]=b[i][j]; rep(i,1,4) base2.m[i][1]=c[i]; ll n=read(); if(n<=9){ println(ans[n]); continue; } base=qmod(base,n-9); Mat res=base*base2; println((res.m[1][1]+MOD)%MOD); } return 0; }
感受這回比賽得背鍋啊orz