1. 程式人生 > >HDU 6030 Happy Necklace (遞推+矩陣快速冪)

HDU 6030 Happy Necklace (遞推+矩陣快速冪)

思路:

首先我們發現,當滿足素數區間2,素數區間3的條件之後,下一個素數區間5乃至於之後的所有都會滿足。(因為滿足素數區間2,素數區間3的條件更強)
然後我們假設現在有一個數目為n的方案數為f(n)
(假設紅的為1 藍的為0 從而將此題簡化成滿足1的個數大於等於0的個數的一個二進位制字串的方案數)
那麼我們考慮一下,能否從f(n-1)轉移到f(n)呢?
考慮這個n-1位,後邊如果加一個1,那麼一定符合條件。(所以方案數可以加一個f(n-1))
那。。。如果最後一位加的是0呢?
此時我們不難發現,倒數第二位一定是1,只有這樣才能滿足素數區間2的條件。
進而得出倒數第三位一定是1,否則不能滿足素數區間3的條件。
所以我們得出結論:f(n) = f(n-1) + f(n-3)
但是還沒完,n的範圍太大,我們無法將遞推得到的結果儲存起來。然而每次都遞推又太慢,所以:矩陣快速冪。
構造一個3*3的矩陣:

Fn00Fn100Fn200(1)
然後構造一個右乘的矩陣
101100010(2)
矩陣1每次乘以矩陣2之後,n就會增長1,但是矩陣的格式不變,所以我們可以通過多次乘2矩陣來實現遞推。而加速就在2矩陣的快速冪。
#include <cstdio>
#include <iostream>
#include <string.h>
#include <queue>
#include <algorithm>
typedef long long int lli;
using namespace std;

#define N 3
const lli mod = 1e9
+7;//注意這裡要是沒有const會慢3~4倍。 struct mat{ lli mat[N][N]; }; mat operator *(mat &a,mat &b){ mat c; memset(c.mat,0,sizeof(c.mat)); for(int k = 0;k<N;++k){ for(int i = 0;i<N;++i){ for(int j=0;j<N;++j){ c.mat[i][j] += a.mat[i][k]*(b.mat[k][j] % mod) % mod; } } } return
c; } mat qp(mat a,lli k){ mat c; for(int i=0;i<N;i++) for(int j =0;j<N;j++) c.mat[i][j] = (i==j); for(;k;k>>=1){ if(k&1) c=c*a; a = a*a; } return c; } int main(){ int t; cin>>t; lli n; while(t--){ scanf("%lld",&n); if(n == 1){ printf("1\n"); continue; } mat a = {1,1,0,0,0,1,1,0,0}; mat b = {6,4,3,0,0,0,0,0,0}; a = qp(a,n-2); b = b * a; printf("%lld\n",b.mat[0][2] % mod); } }