hdu 3519 递推+矩阵快速幂
题目:http://acm.hdu.edu.cn/showproblem.php?pid=3519
题意:
有n个硬币排成一排,有正有反,问有2个以上连续的相同硬币有几种方案
分析:
典型的推公式用矩阵快速幂求解的题目。
(偷个懒,贴个别人的推导)
长度为 n 的 01 串一共有 2^n 种不同的排列方法 ,设 f(n) 为长度是 n 的不包含连续 3 个或以上相同的 1 或 0 的 01 串 ,则 f(1)=2,f(2)=4,f(3)=6,f(4)=10
当 n>4 的时候 , 分情况考虑 :
1、 如果是以 00 或者 11 结尾 , 则分别有 f(n-2)/2 种情况 , 加起来就是 f(n-2) 种 .
2、 如果是以 01 或者 10 结尾 , 则第 n 个字符要和第 n-1 个字符不一样 , 那么分别有 f(n-1)/2 种 , 加起来就是 f(n-1)
则统计起来就是 f(n)=f(n-1)+f(n-2), 题目要求的是包含连续三个相同的 0 或 1 串的串数 , 那就是用 a[n]=(2^n-f(n))%10007.
然而这样还不好求 , 先不看 %10007, 转换成递推公式是 a[n]=a[n-1]+a[n-2]+2^(n-2),
转换成矩阵 :
a[n] 1 1 1 a[n-1]
a[n-1] = 1 0 0 * a[n-2]
2^(n-1) 0 0 2 2^(n-2)
这样就可以用矩阵幂快速算出 a[n], 复杂度为 O(logn)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=3;
const int mod=10007;
struct Mat{
int mat[N][N];
};
Mat mul(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]=(c.mat[i][j]+a.mat[i][k]*b.mat[k][j])%mod;
return c;
}
Mat qmod(Mat a,int 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=mul(c,a);
a=mul(a,a);
}
return c;
}
int main()
{
int n;
int b[]={0,0,0,2,6};
while(~scanf("%d",&n)){
Mat a;
a.mat[0][0]=a.mat[0][1]=a.mat[0][2]=1;
a.mat[1][0]=1;a.mat[1][1]=a.mat[1][2]=0;
a.mat[2][0]=a.mat[2][1]=0;a.mat[2][2]=2;
if(n<=4){
printf("%d\n",b[n]);continue;
}
Mat c=qmod(a,n-4);
int ans=c.mat[0][0]*6+c.mat[0][1]*2+c.mat[0][2]*8;
printf("%d\n",ans%mod);
}
return 0;
}