【BM递推】【矩阵快速幂】Poor God Water

%%% dls


Poor God Water

题目描述

God Water likes to eat meat, fish and chocolate very much, but unfortunately, the doctor tells him that some sequence of eating will make them poisonous.
Every hour, God Water will eat one kind of food among meat, fish and chocolate. If there are 3 continuous hours when he eats only one kind of food, he will be unhappy. Besides, if there are 3 continuous hours when he eats all kinds of those, with chocolate at the middle hour, it will be dangerous. Moreover, if there are 3 continuous hours when he eats meat or fish at the middle hour, with chocolate at other two hours, it will also be dangerous.
Now, you are the doctor. Can you find out how many different kinds of diet that can make God Water happy and safe during N hours? Two kinds of diet are considered the same if they share the same kind of food at the same hour. The answer may be very large, so you only need to give out the answer module 1000000007.

 

 

输入

The fist line puts an integer T that shows the number of test cases. (T≤1000)
Each of the next T lines contains an integer N that shows the number of hours. (1≤N≤10^10)

 

输出

For each test case, output a single line containing the answer.

 

样例输入

复制样例数据

3
3
4
15

样例输出

20
46
435170

题意:

有n天,有三种食物:鱼,巧克力,肉,问你有多少种饮食方案

约束条件是:①:连续三天不能吃同一种食物;②:连续三小时中,中间是巧克力,两边是鱼和肉,也不行

③:连续三小时,中间是鱼或肉,两边是巧克力的话也不行


方法:

①:杜教的BM递推(原来大佬的世界都是自己发明代码吗。。瑟瑟发抖)

    对于线性的递推式,如果取模是个质数的话,就可以套用这个模板了(orz)

②: 根据关系写出矩阵的状态转移来,然后有矩阵快速幂求解即可

 


先放杜教的BM模板下改编的这道题过得代码

 

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const ll mod=1000000007;
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll _,n;
namespace linear_seq{
    const int N=10010;
    ll res[N],base[N],_c[N],_md[N];
    vector<ll> Md;
    void mul(ll *a,ll *b,int k)
    {
        rep(i,0,k+k) _c[i]=0;
        rep(i,0,k) if (a[i]) rep(j,0,k) _c[i+j]=(_c[i+j]+a[i]*b[j])%mod;
        for (int i=k+k-1;i>=k;i--) if (_c[i])
            rep(j,0,SZ(Md)) _c[i-k+Md[j]]=(_c[i-k+Md[j]]-_c[i]*_md[Md[j]])%mod;
        rep(i,0,k) a[i]=_c[i];
    }
    int solve(ll n,VI a,VI b)
    {
        ll ans=0,pnt=0;
        int k=SZ(a);
        assert(SZ(a)==SZ(b));
        rep(i,0,k) _md[k-1-i]=-a[i];_md[k]=1;
        Md.clear();
        rep(i,0,k) if (_md[i]!=0) Md.push_back(i);
        rep(i,0,k) res[i]=base[i]=0;
        res[0]=1;
        while ((1ll<<pnt)<=n) pnt++;
        for (int p=pnt;p>=0;p--)
        {
            mul(res,res,k);
            if ((n>>p)&1)
            {
                for (int i=k-1;i>=0;i--) res[i+1]=res[i];res[0]=0;
                rep(j,0,SZ(Md)) res[Md[j]]=(res[Md[j]]-res[k]*_md[Md[j]])%mod;
            }
        }
        rep(i,0,k) ans=(ans+res[i]*b[i])%mod;
        if (ans<0) ans+=mod;
        return ans;
    }
    VI BM(VI s) {
        VI C(1,1),B(1,1);
        int L=0,m=1,b=1;
        rep(n,0,SZ(s)) {
            ll d=0;
            rep(i,0,L+1) d=(d+(ll)C[i]*s[n-i])%mod;
            if (d==0) ++m;
            else if (2*L<=n) {
                VI T=C;
                ll c=mod-d*powmod(b,mod-2)%mod;
                while (SZ(C)<SZ(B)+m) C.pb(0);
                rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
                L=n+1-L; B=T; b=d; m=1;
            } else {
                ll c=mod-d*powmod(b,mod-2)%mod;
                while (SZ(C)<SZ(B)+m) C.pb(0);
                rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
                ++m;
            }
        }
        return C;
    }
    int gao(VI a,ll n){
        VI c=BM(a);
        c.erase(c.begin());
        rep(i,0,SZ(c)) c[i]=(mod-c[i])%mod;
        return solve(n,c,VI(a.begin(),a.begin()+SZ(c)));
    }
};
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int t;
    cin>>t;
    while(t--)
    {
        cin>>n;
        int arr[17]={3,9,20,46,106,244,560,1286,2956,6794};
        vector<int> v(arr,arr+10);
        cout<<linear_seq::gao(v,n-1)%mod<<endl;
    }
}

 

 

 


 

矩阵快速幂模板

#include<bits/stdc++.h>
typedef long long ll;
const int mod=1000000007;
const int N=9;
using namespace std;
struct matrix
{
    ll m[N][N];
};
matrix mul(matrix a,matrix b)
{
    matrix ans;
    memset(ans.m,0,sizeof(ans.m));
    for(int i=0; i<N; i++)
        for(int j=0; j<N; j++)
            for(int k=0; k<N; k++)
            {
                ans.m[i][j]+=a.m[i][k]*b.m[k][j];
                ans.m[i][j]%=mod;
            }
    return ans;
}
matrix qpow(matrix a,ll b)
{
    matrix ans,tmp=a;
    for(int i=0; i<N; i++)
        for(int j=0; j<N; j++)
            ans.m[i][j]=i==j?1:0;
    while(b)
    {
        if(b&1) ans=mul(ans,tmp);
        b>>=1;
        tmp=mul(tmp,tmp);
    }
    return ans;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin>>t;

    while(t--)
    {
        ll n;
        cin>>n;
        if(n<=2)
        {
            if(n==1)
                cout<<"3"<<endl;
            else if(n==2)
                cout<<"9"<<endl;
        }
        else{
        matrix ret=
        {
            0,0,0, 1,0,0, 1,0,0,
            1,0,0, 0,0,0, 1,0,0,
            1,0,0, 1,0,0, 1,0,0,

            0,1,0, 0,1,0, 0,0,0,
            0,1,0, 0,0,0, 0,1,0,
            0,0,0, 0,1,0, 0,1,0,

            0,0,1, 0,0,1, 0,0,1,
            0,0,1, 0,0,0, 0,0,1,
            0,0,1, 0,0,1, 0,0,0
        };
        matrix ans;
        ans=qpow(ret,n-2);
        ll sum=0;
        for(int i=0;i<N;i++)
            for(int j=0;j<N;j++)
                sum=(sum+ans.m[i][j])%mod;
        cout<<sum<<endl;
        }
    }
    return 0;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值