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;
}
posted @ 2016-03-22 20:48  HARD_UNDERSTAND  阅读(135)  评论(0编辑  收藏  举报