题目1 : 骨牌覆盖问题·二
时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
上一周我们研究了2xN的骨牌问题,这一周我们不妨加大一下难度,研究一下3xN的骨牌问题?
所以我们的题目是:对于3xN的棋盘,使用1x2的骨牌去覆盖一共有多少种不同的覆盖方法呢?
首先我们可以肯定,奇数长度一定是没有办法覆盖的;对于偶数长度,比如2,4,我们有下面几种覆盖方式:
输入
第1行:1个整数N。表示棋盘长度。1≤N≤100,000,000
输出
第1行:1个整数,表示覆盖方案数 MOD 12357
- 样例输入
-
62247088
- 样例输出
-
4037
#include <iostream> #include <vector> using namespace std; #define modnum 12357 typedef vector<long long> matrow; typedef vector<matrow> mat; mat build(long long x,long long y,long long a[]) { mat ans; for(long long i=0;i<x;i++) { ans.push_back(matrow()); for(long long j=0;j<y;j++) { ans[i].push_back(a[i*y+j]); } } return ans; } mat multi(const mat mat1,const mat mat2) { long long temp=0; long long rownum=mat1.size(); long long colnum=mat2[0].size(); long long nummul=mat1[0].size(); mat ans; for(long long i=0;i<rownum;i++) { ans.push_back(matrow()); for(long long j=0;j<colnum;j++) { temp=0; for(long long t=0;t<nummul;t++) temp+=mat1[i][t]*mat2[t][j]; ans[i].push_back(temp%modnum); } } return ans; } int main() { long long N; long long a[]={3,1,2,1}; long long b[]={3,2}; cin>>N; if(N%2==1){ cout<<0<<endl; return 0; } mat ans=build(2,1,b); mat temp=build(2,2,a); long long n=N/2-1; if(n==0)cout<<3<<endl; if(n==1)cout<<11<<endl; if(n!=1&&n!=0) { while(n) { if(n&1==1) ans=multi(temp,ans); temp=multi(temp,temp); n=n>>1; } } cout<<ans[0][0]<<endl; return 0; }
这一期的问题和上一期的一样,也是使用矩阵乘法加速,
用Xn表示N的排列总数
用Tn表示N的特殊排列总数(特殊排列:最后一列有竖直骨牌如:n=2时 第二第三个骨牌。 n=4时2 4 6 7 8 10 11)
N+2的排列总数 = “N的排列总数 拼接上n=2时的排列” + “N的特殊排列 拼接上n=2时第一个骨牌 并进行微调”
( “N的特殊排列 接上n=2时第一个骨牌 并进行微调” 参见n=4时 4和8 ,通过n=4时 9和5调整获得 )
Xn+2=Xn*3+Tn
N+2的特殊排列=“N的排列总数,拼接上n=2时 2 3 骨牌”+ “N的特殊排列 ,拼接上n=2时第一个骨牌 并进行微调”
Tn+2=Xn*2+Tn
用矩阵表示:
|Xn+2m| = |3 1| ^m-1 |3|
|Tn+2m| |2 1| |2|