矩阵快速幂的复习。。。

很久很久都没有做过矩阵快速幂的题目了,昨天做了一个竟然丝毫想不起来。。。不由让我想复习一下。

快速幂的原理就不必多说,为了实现某些题目中的答案,答案又依赖与某些类似斐波那契数列的关系,我们就可以利用矩阵来实现 这个线性递推过程,利用快速幂的原理来缩短这个很长的线性过程。

1.先是最简单的题型
比如 a[n]= x*a[n-1] + y *a[n-2]
|a[n] |………… |x y |……….. |a[n-1] |…… |x* a [n-1] + y*a[n-2] |
|a[n-1] | =…..| 1 0 |.. *….. |a[n-2] | = ..|1*a[n-1]…………………..|

2印象有点深刻的题目,沈阳现场赛的题
http://acm.hdu.edu.cn/showproblem.php?pid=5950

F(n) = 2*F(n-2) + F(n-1) + n^4 和F(1) = a,F(2) = b;给定一个N,求F(N)等于多少?
我们可以发现这个递推里面 只有 n^4是不满足线性的
那么我们就要把这个变成线性所以要和n-1 相关联,于是n=(n-1 +1)^4
这里写图片描述

3.http://codeforces.com/contest/821/problem/E

k 1e18 可以知道,每一条线段的内(不同位置)的种数可以由长度、位置、推断出来

由于n 100  c 15,让人不由得往dp上想啊。

而且这个线段的dp肯定满足线性(从前往后)的关系。
所以我们可以枚举 每一段线段的起点,然后给这一段线段的终点添加方法数

可以得出:

                      +x     +4x
             +x       +3x    +10x
         +x  +2x      +6x    +16x
   +x    +x  +3x      +7x    +19x
         +x  +2x      +6x    +16x
             +x       +3x    +10x
                      +x     +4x
我们换成矩阵的形式:
    0    0   0       x    4x
    0    0   x       3x   10x
    0    x   2x      6x   16x
    x    x   3x      7x   19x
    0    x   2x      6x   16x
    0    0   x       3x   10x
    0    0   0       x    4x
我们可以得到 dp[x][y]= dp[x-1][y-1]+dp[x-1][y]+dp[x-1][y+1]
有一个简单的方法是用线性 的累加平推过去。。。。但是肯定会TLE
所以我们用 矩。。。矩阵快速幂。 ,利用快速幂的原理可以缩短这一累乘的过程

至于矩阵  我们把所有的都看成是16*16 来做
注意把实际上无法到达的地方归零,特别是线段与线段交接时
而且特别注意的一个地方就是需要把操作矩阵相应的地方也归零,这里改了比较久

#define ll __int64
#define mod 1000000007
struct matr
{
   __int64 an[20][20];
}flag,temp,ans;
void print(matr x){
    for(int i=0;i<=15;i++){
        for(int j=0;j<=15;j++){
            printf("%I64d ",x.an[i][j]);
        }
        printf("\n");
    }
    printf("\n\n");
}
matr mul(matr a,matr b )  //注意顺序:aXb
{
    matr c;

    for(int i=0;i<=15;i++)
        for(int j=0;j<=15;j++)
        {
            c.an[i][j]=0;
            for(int k=0;k<=15;k++)
               c.an[i][j]+=a.an[i][k]*b.an[k][j];
            c.an[i][j]%=mod;
        }
    return  c;
}
__int64 fast(__int64 n,int H)
{
     temp=flag;
     for(int i=0;i<H;i++){   //不能忘记吧temp相应的行数也归零啊
         for(int j=0;j<=15;j++)
             temp.an[i][j]=0;
     }
     while(n)
     {
         if(n%2==1)
         {
             ans=mul(temp,ans);
             for(int j=0;j<H;j++)
                ans.an[j][0]=0;
         }
         n/=2;
         temp=mul(temp,temp);
//         printf("n=%I64d\n",n);
//         printf("ans=\n");
//         print(ans);
//         printf("temp=\n");
//         print(temp);
     }
     return ans.an[0][0];
}

ll a[105],b[105];
int c[105];
int main(){
    freopen("1.txt","r",stdin);
    int n;
    ll k;
    scanf("%d %I64d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%I64d %I64d %d",&a[i],&b[i],&c[i]);
    }
    for(int i=0;i<=15;i++){
        for(int j=0;j<=15;j++){
            ans.an[i][j]=0;
            flag.an[i][j]=0;
            if(abs(j-i)<=1)
                flag.an[i][j]=1;
        }
    }
    ans.an[15][0]=1;
    for(int i=1;i<=n;i++){
//      printf("%dTH --line\n",i);
        ll L=b[i]-a[i];
        if(k <=b[i]){
            L=k-a[i];
        }
        for(int j=0;j< 15-c[i];j++)
            ans.an[j][0]=0;
        fast(L,15-c[i]);
        for(int j=0;j< 15-c[i];j++)
            ans.an[j][0]=0;
    }
    printf("%I64d\n",ans.an[15][0]);
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值