bzoj5015 [Snoi2017]礼物

题目

SN,陕西OI啊,作为第一次参加省选,还是很紧张的啊,当时居然连这道题都只写了80分的,而且还挂成了50分233,宝宝心里苦啊。。。

最后,成功被卡线rk10,就这样与省队失之交臂了,好可惜啊。。

然后过了,一个月,打算再写一写,发现。。woc,不就是一个矩阵吗233。但是居然认为不是线性的递推式就不能做,还是太年轻啊。然后,比较麻烦地推了推矩阵,过了大样例。。但苦于SNOI没有数据流出233,只能对拍了,但由于暴力的“标程”,不能算太大的,不知是否会玄学爆int。。

至少,对拍了十多分钟,都是对的,就当作是对的了吧。。。

之后,发现今天bzoj上居然有数据了,测一发,居然一遍过了233。

怕是由于递推而没有进省队的我呀233。

贴一个程序吧。

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL mod=1e9+7;
LL n,k;
LL s[1000001];
LL fac[101],inv[101];
struct cube{
    int h;
    int l;
    int v[15][15];
};
LL mul(LL A,LL B)
{
    int fl=0;
    if(A<0)fl++,A=-A;
    if(B<0)fl++,B=-B;
    LL Ans=0;
    while(B)
    {
        if(B&1)Ans=(Ans+A)%mod;
        B=B>>1;
        A=(A+A)%mod;
    }
    if(fl&1)Ans=-Ans;
    return Ans;
}
LL ksm(LL A,LL B)
{
    LL Ans=1;
    while(B)
    {
        if(B&1)Ans=mul(Ans,A);
        B=B>>1;
        A=mul(A,A);
    }
    return Ans;
}
void init()
{
    fac[0]=1;
    for(int i=1;i<=100;i++)
        fac[i]=(fac[i-1]*i)%mod;
    for(int i=0;i<=100;i++)
        inv[i]=ksm(fac[i],mod-2);
}
void cube_mul(cube A,cube B,cube &C)
{
    C.h=A.h,C.l=B.l;
    for(int i=1;i<=C.h;i++)
        for(int j=1;j<=C.l;j++)
            C.v[i][j]=0;
    for(int i=1;i<=C.h;i++)
        for(int j=1;j<=C.l;j++)
            for(int k=1;k<=A.l;k++)
                C.v[i][j]=(C.v[i][j]+mul(A.v[i][k],B.v[k][j]))%mod;
}
void cube_ksm(cube A,LL B,cube &Ans)
{
    Ans.h=Ans.l=A.h;
    for(int i=1;i<=Ans.h;i++)
        for(int j=1;j<=Ans.l;j++)
            Ans.v[i][j]=0;
    for(int i=1;i<=Ans.h;i++)Ans.v[i][i]=1;
    while(B)
    {
        if(B&1)cube_mul(A,Ans,Ans);
        B=B>>1;
        cube_mul(A,A,A);
    }
}
LL C(LL A,LL B)
{
    return mul(mul(fac[A],inv[B]),inv[A-B])%mod;
}
void get_cube(cube &A,cube &B)
{
    A.h=A.l=k+2;
    for(int i=1;i<=A.h;i++)
        for(int j=1;j<=A.l;j++)
            A.v[i][j]=0;
    A.v[1][1]=2,A.v[2][1]=1;
    for(int i=2;i<A.h;i++)
        A.v[i][i]=A.v[i+1][i]=1;
    A.v[A.h][A.h]=1;

    B.h=1,B.l=k+2;
    for(int i=1;i<=B.l;i++)
        B.v[1][i]=0;
    B.v[1][1]=1;
    for(int i=2;i<=k+2;i++)
    {
        for(int j=1;j<i;j++)
        {
            LL x,y,z;
            x=ksm(-1,j+1);
            y=C(i-2,j-1);
            z=ksm(i-j+1,k);
            B.v[1][i]=(B.v[1][i]+x*y*z)%mod;
        }
    }
}
cube A,B;
int main()
{
    freopen("in.txt","r",stdin);
    cin>>n>>k;
    init();
    if(n==1){
        cout<<1;
        return 0;
    }
    get_cube(B,A);
    cube_ksm(B,n-2,B);
    cube_mul(A,B,A);
    LL k1=A.v[1][1];
    get_cube(B,A);
    cube_ksm(B,n-1,B);
    cube_mul(A,B,A);
    LL k2=A.v[1][1];
    cout<<(k2-k1+mod)%mod;  
    return 0;
}

跑得好慢啊。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值