比赛的时候规律题是很常见的事情 ,所以这里做下总结。
解题分几个步骤;
1 : 首先规律题的数据规模是大的 , 可以说如果结果是数字样的话,而且数据大很有可能的规律;
2 : 暴力打表找到前面10位数字;
2(1) : 如果足够强大可以根据某种灵性的解法得出答案(我是不够灵的。。)
3 :如果足够强大可以暴力推出公式;
3(1) : 运用技巧性得出结果
4:运用矩阵快速幂
例如: HDU 6185
用没有数量限制2×1和1×2的矩形去铺一块4×n的矩形,有多少种方法
n最大1e^18
1: 打出暴力程序(参考状态压缩DP瓷砖覆盖):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
*/ #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <queue> #include <algorithm> #include <map> #include <cmath> #include <iomanip> #define INF 99999999 typedef long long LL; using namespace std; const int MAX=(1<<11)+10; int n,m; LL temp[MAX],dp[MAX],bin[15]; bool mark[MAX]; bool check(int i){ while(i){ if(i&1){ i>>=1; if(!(i&1))return false;//第j列是1则第j+1列必须是1 i>>=1;//继续判断下一列 }else i>>=1;//继续判断下一列 } return true; } void Init(){ memset(mark,false,sizeof mark); memset(temp,0,sizeof temp); for(int i=0;i<bin[m];++i){//初始化第一行和可以到达什么状态 if(check(i))temp[i]=1,mark[i]=true; } } void DP(){ for(int k=2;k<=n;++k){ for(int i=0;i<bin[m];++i)dp[i]=0; for(int i=0;i<bin[m];++i){ for(int j=0;j<bin[m];++j){ if((i|j) != bin[m]-1)continue;//每一位或之后必须每一位是1(综合前面3种情况和分析可知) if(!mark[i&j])continue;//由初始化和前面分析三种情况分析可知i&j必须得到和初始化可以到达的状态一样才行 dp[i]+=temp[j];//i可以从j到达,则增加j的方案数 } } for(int i=0;i<bin[m];++i)temp[i]=dp[i]; } } int main(){ bin[0]=1; for(int i=1;i<12;++i)bin[i]=2*bin[i-1]; while(~scanf("%d%d",&n,&m),n+m){ if(n<m)swap(n,m);//始终保持m<n,提高效率 Init(); DP(); printf("%lld ",temp[bin[m]-1]);//输出最后一行到达时的状态必须全部是1 } return 0; }
1 1
2 5
3 11
4 36
5 95
6 281
7 781
8 2245
9 6336
10 18061
然后就可以得出前面的10项数字 , 然后
2 :
(1)暴力推出线性公式 ;
(2) 根据这十个数利用高斯消元,先假设是f(n)=f(n-1)*a+f(n-2)*b (学到了)
如果高斯消元得到的结果不是整数接着试
最终f(n)=f(n-1)*a+f(n-2)*b+f(n-3)*c+f(n-4)*d
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//f(n)=f(n-1)*a+f(n-2)*b+f(n-3)*c+f(n-4)*d情况下的高斯消元 #include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #include <cstring> #include <vector> using namespace std; const int maxn=105; typedef double Matrix[maxn][maxn]; Matrix A,S; //n是方程的个数 void gauss(Matrix A,int n) { int i,j,k,r; for(int i=0; i<n; i++) { r=i; for( j=i+1; j<n; j++) if(fabs(A[j][i])>fabs(A[r][i]))r=j; if(r!=i) for(j=0; j<=n; j++)swap(A[r][j],A[i][j]); for(k=i+1; k<n; k++) { double f=A[k][i]/A[i][i]; for(j=i; j<=n; j++) A[k][j]-=f*A[i][j]; } } for(i=n-1; i>=0; i--) { for(j=i+1; j<n; j++) A[i][n]-=A[j][n]*A[i][j]; A[i][n]/=A[i][i]; } } int main() { memset(A,0,sizeof(A)); A[0][0]=95,A[0][1]=36,A[0][2]=11,A[0][3]=5,A[1][0]=6336,A[1][1]=2245,A[1][2]=781; A[1][3]=281,A[2][0]=781,A[2][1]=281,A[2][2]=95,A[2][3]=36,A[3][0]=2245,A[3][1]=781; A[3][2]=281,A[3][3]=95,A[0][4]=281,A[1][4]=18061,A[2][4]=2245,A[3][4]=6336; gauss(A,4); for(int i=0; i<4; i++) { printf("%8.2f ",A[i][4]); } return 0; }
运行得到结果
1.00
5.00
1.00
-1.00
所以f(n)=f(n-1)+f(n-2)*5+f(n-3)-f(n-4)
然后根据递推公式用矩阵快速幂解决
值得注意的是由于矩阵系数有负数,在做矩阵相乘时注意+mod再取膜
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <cstdio> #include <vector> #include <queue> #include <cstring> #include <algorithm> using namespace std; #define mst(a,b) memset((a),(b),sizeof(a)) #define rush() int T,scanf("%d",&T),while(T--) typedef long long ll; const int maxn = 4; const ll mod = 1000000007; const int INF = 0x3f3f3f3f; struct Matrix { ll temp[maxn][maxn]; } a; void init() { for(int i=0;i<maxn;i++) for(int j=0;j<maxn;j++) { a.temp[i][j]=0; } a.temp[0][0]=a.temp[0][2]=1; a.temp[0][1]=5; a.temp[0][3]=-1; a.temp[1][0]=a.temp[2][1]=a.temp[3][2]=1; } Matrix mul(Matrix a,Matrix b) { Matrix ans; for (int i=0; i<maxn; i++) for (int j=0; j<maxn; j++) { ans.temp[i][j]=0; for (int k=0; k<maxn; k++) { ans.temp[i][j]+=(a.temp[i][k]*b.temp[k][j]+mod)%mod; //特别注意 ans.temp[i][j]%=mod; } } return ans; } void fun(Matrix ans,ll k) { for(int i=0; i<maxn; i++) for(int j=0; j<maxn; j++) a.temp[i][j]=(i==j); while(k) { if(k%2) a=mul(a,ans); ans=mul(ans,ans); k/=2; } } int main() { Matrix t; ll n; for(int i=0;i<maxn;i++) for(int j=0;j<maxn;j++) { t.temp[i][j]=0; } t.temp[0][0]=36; t.temp[1][0]=11; t.temp[2][0]=5; t.temp[3][0]=1; while(~scanf("%I64d",&n)) { init(); if(n<=4) { printf("%I64d ",t.temp[4-n][0]); continue; } fun(a,n-4); a=mul(a,t); ll ans=a.temp[0][0]%mod; printf("%I64d ",ans); } return 0; }
(3)灵性的解法:https://blog.csdn.net/elbadaernu/article/details/77825979
3:
上面的步骤都是为了得出:f(n)=f(n-1)+f(n-2)*5+f(n-3)-f(n-4)
知道了这个线性的方程后 , 写出矩阵快速幂
| 1 5 1 -1 | | F[n-1] | | F[n] |
| 1 0 0 0 | * | F[n-2] | = | F[n-1] |
| 0 1 0 0 | |F[n-3] | | F[n-2] |
| 0 0 1 0 | | F[n-4]| | F[n-3] |
4:修改快速迷模板:
然后AC:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <cstdio> #include <vector> #include <queue> #include <cstring> #include <algorithm> using namespace std; #define mst(a,b) memset((a),(b),sizeof(a)) #define rush() int T,scanf("%d",&T),while(T--) typedef long long ll; const int maxn = 4; const ll mod = 1000000007; const int INF = 0x3f3f3f3f; struct Matrix { ll temp[maxn][maxn]; } a; void init() { for(int i=0;i<maxn;i++) for(int j=0;j<maxn;j++) { a.temp[i][j]=0; } a.temp[0][0]=a.temp[0][2]=1; a.temp[0][1]=5; a.temp[0][3]=-1; a.temp[1][0]=a.temp[2][1]=a.temp[3][2]=1; } Matrix mul(Matrix a,Matrix b) { Matrix ans; for (int i=0; i<maxn; i++) for (int j=0; j<maxn; j++) { ans.temp[i][j]=0; for (int k=0; k<maxn; k++) { ans.temp[i][j]+=(a.temp[i][k]*b.temp[k][j]+mod)%mod; //特别注意 ans.temp[i][j]%=mod; } } return ans; } void fun(Matrix ans,ll k) { for(int i=0; i<maxn; i++) for(int j=0; j<maxn; j++) a.temp[i][j]=(i==j); while(k) { if(k%2) a=mul(a,ans); ans=mul(ans,ans); k/=2; } } int main() { Matrix t; ll n; for(int i=0;i<maxn;i++) for(int j=0;j<maxn;j++) { t.temp[i][j]=0; } t.temp[0][0]=36; t.temp[1][0]=11; t.temp[2][0]=5; t.temp[3][0]=1; while(~scanf("%I64d",&n)) { init(); if(n<=4) { printf("%I64d ",t.temp[4-n][0]); continue; } fun(a,n-4); a=mul(a,t); ll ans=a.temp[0][0]%mod; printf("%I64d ",ans); } return 0; }