戰場由若干單位正方形積木組成。積木佔據了連續的若干列,且圖形周長等於它最小包圍矩形的周長。假設戰場的圖形周長爲p,一共有多少種可能的戰場?
戰場不能恰好爲一個矩形。
例如,p<8時沒有符合要求的戰場,p=8時有2種戰場:
p=10有9種戰場:
要求輸出方案總數模987654321的值。
7
8
9
10
0Sample Output
0
2
0
9
Hint
思路:
根據題目意思可以得知這是一道找規律求遞推公式的題目,所以,需要從前往後推,憑空想象只能是浪費時間。
分析:
n=4 戰場數目爲1(圖形即一個單位正方形)
n=6 戰場數目爲2(圖形爲兩個單位正方形橫着排列與豎着排列兩種)
n=8 戰場數目爲5
奇數無解
以上數目都是包括組成圖形爲矩形的,最後要減去這些數目。
從以上題目給出的圖和已經得出的結論嘗試推導遞推公式,周長爲n的一個圖形如果左邊有一個單個的正方形,減去他就是周長n-2的一個圖形,同理右邊與最上方
也是這個道理,而左右各減去一個正方形即爲周長n-4的圖形 這裏發現n-4的圖形包括在n-2中重複了,故遞推公式爲 f(n)=3*f(n-2)-f(n-4);
現在的問題是如何求得f(n).
這裏用到了斐波拉契數列進行輔助。
在Fibonacci整數序列中,對於n≥2,F 0 = 0,F 1 = 1,F n = F n -1 + F n -2。例如,斐波那契序列的前十項是:
0,1,1,2,3,5,8,13,21,34,...
斐波納契序列的另一個公式是
。
求斐波拉契數列的f(n)我們用到了矩陣快速冪,而這道題同樣的也可以運用 矩陣快速冪進行求解,所以可以得知公式f的第一項爲n=4, 後面的隨即求出。然後如何把這個
遞推公式與斐波拉契數列的遞推公式聯繫起來,思考與觀察後可以猜測f(n)(n>=4)=F(n-4+1),所以可以先令m=n-4,求得pow(m),然後返回F(m+1)即爲所求的的值KK1(
沒有減去組成恰好爲一個矩形的個數),所以還要求出一個公式爲n/2-1,代表的是邊長爲n的矩形由1*1的正方形組成總共有多少種組成方法,所以最後所求的答案爲
KK1-n/2+1.
PS:第一道矩陣非常規題,自己還差的太遠,想了五個小時,一開始思路就錯了,難點在於斐波拉契數列 的聯繫。吸取教訓,繼續。
#include<stdio.h> #include<cstring> #include<iostream> using namespace std; #define ll long long #define mod 987654321 struct ju { ll m[2][2]; }ans,base; ju multi(ju a,ju b) { ll temp[2][2]; for(int i=0;i<2;i++) { for(int j=0;j<2;j++) { temp[i][j]=a.m[i][j]; } } for(int i=0;i<2;i++) { for(int j=0;j<2;j++) { a.m[i][j]=0; for(int w=0;w<2;w++) a.m[i][j]+=(temp[i][w]*b.m[j][w])%mod; } } return a; } int pow(int n) { base.m[0][0] = base.m[0][1] = base.m[1][0] = 1; base.m[1][1] = 0; ans.m[0][0] = ans.m[1][1] = 1; // ans 初始化爲單位矩陣 ans.m[0][1] = ans.m[1][0] = 0; //n-=4; while(n) { if(n&1) { ans=multi(base,ans); } base=multi(base,base); n>>=1; } return ans.m[0][0]; } int main() { int n; while(scanf("%d",&n)&&n!=0) { if(n<8||n&1) printf("0\n"); else { ll kk1=(pow(n-4)-n/2+1)%mod; if(kk1<0) kk1+=mod; printf("%lld\n",kk1%mod); } } return 0; }