[hdu-6395]Sequence 分块+矩阵快速幂

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6395

因为题目数据范围太大,又存在递推关系,用矩阵快速幂来加快递推。

每一项递推时  加的下取整的数随着n变化,但因为下取整有连续性(n一段区间下取整的数是相同的),可以分块,相同的用矩阵快速幂加速

想了好久。。如果最小的开始的值是【p/i】的数为i,那连续的一段长度是【p/(p/i)】-i+1,但为什么分段数是根号n级别啊?。。。

套矩阵快速幂,时间复杂度O(sqrt(n) * log(n))

F1F2Fn===ABCFn2+DFn1+Pn
F1F2Fn===ABCFn2+DFn1+Pn

一个递推关系式可以用矩阵乘法来表示递推关系

有两种写法

至于怎么求特征值矩阵(用来加速的那个矩阵),就是根据关系式,观察前后两个的变化与联系填就好了

为了写着省事用了第一种,但其实它复杂度高,推荐第二种

坑:

1.n=1 和 n=2 要特判

2.如果n>p,最后加的下取整的数为0,要特判,且特判的时候,注意长度不是【n-p】,p可能比2小,而我们矩阵的第一项是从F2 开始的,应为[n-max(2,p)]

3.矩阵快速幂三层循环的变量不要写错,。。一开始把第三层的k写成 i 死循环了

4.运算符重载后不要用scanf输入,在一些oj上会超时

5.max、min函数比较的是同一种变量

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long ll;
const int p=1e9+7;
ll aa,b,c,d,mo,n;
struct matrix
{
    ll a[4][4];
    ll* operator [](int x) {return a[x];}//这里是*不是&
    matrix operator *(matrix b)//重载*运算符 
    {
        matrix c;
        memset(c.a,0,sizeof(c.a));
        ll tmp;
        for (int i=1;i<=3;i++) {
            tmp=0;
            for (int j=1;j<=3;c[i][j]=tmp%p,tmp=0,j++)
            for (int k=1;k<=3;k++) tmp+=(a[i][k]*b[k][j])%p;  
        }
            //前提p^3不爆LL可以在第二层再取模 
        return c;
    }
    matrix operator ^(ll T)
    {
        matrix a=*this,b;//this是指针,所以是*this,这样才可以保证原来的a并不改变
        memset(b.a,0,sizeof(b.a));
        for (int i=1;i<=3;i++) b[i][i]=1;
        for (;T;a=a*a,T>>=1) if (T&1) b=b*a;
        return b;
    }
};
matrix ans;
void work(ll ci,ll chang){
    ans[1][3]=chang;
    matrix node;
     node[1][1]=d,node[1][2]=1,node[1][3]=0;
     node[2][1]=c,node[2][2]=0,node[2][3]=0;
     node[3][1]=1,node[3][2]=0,node[3][3]=1;
     matrix ping=node^ci;
     ans=ans*ping;
}
int main(){
    int t;
    scanf("%d",&t);
    for(int p=1;p<=t;p++){
        cin>>aa>>b>>c>>d>>mo>>n;
       if(n==1){
           cout<<aa;continue;//运算符重载后不要用scanf输入
       }
       else if(n==2){
           cout<<b;continue;
       }
       else{
           
        ans[1][1]=b,ans[1][2]=aa;
        for(int i=1;i<=3;i++){
           ans[2][i]=0;ans[3][i]=0;
        }
        for(ll i=3,j;i<=n;i=j+1){
               if(mo/i==0)break;
            j=mo/(mo/i);
               if(n<j)j=n;
            work(j-i+1,mo/i);
        }
           ll w=2;
           if(mo>w)w=mo;
           if(n>mo)work(n-w,0);//一定不要直接用n-mo,如果mo是1会错误,要与2进行大小比较
           cout<<ans[1][1]<<endl;    
       }
    }
    return 0;
}

 再附上一个矩阵快速幂的板子:

struct matrix
{
    int n,m;
    LL a[maxk][maxk];
    LL* operator [](int x) {return a[x];}//这里是*不是&
    matrix operator *(matrix b)
    {
        int i,j,kk;
        matrix c;
        c.n=n,c.m=b.m;
        memset(c.a,0,sizeof(c.a));
        LL tmp;
        for (i=1;i<=n;i++) for (j=1,tmp=0;j<=b.m;c[i][j]=tmp%p,tmp=0,j++)
            for (kk=1;kk<=m;kk++) tmp+=a[i][kk]*b[kk][j];
        return c;
    }
    matrix operator ^(LL T)
    {
        matrix a=*this,b;//this是指针,所以是*this,这样才可以保证原来的a并不改变
        memset(b.a,0,sizeof(b.a));
        b.n=n,b.m=m;
        for (int i=1;i<=n;i++) b[i][i]=1;
        for (;T;a=a*a,T>>=1) if (T&1) b=b*a;
        return b;
    }
};
/*
ll* 返回的是指针,也就是 a[x]是一个指针(相当于返回第x行第0列的数值的指针),a[x][y]是一个数值,在外面调用a[x][y]时,相当于(m.a[x])[y]

F1F2Fn===ABCFn2+DFn1+Pn

猜你喜欢

转载自www.cnblogs.com/conver/p/10433081.html
今日推荐