• BZOJ 3157: 国王奇遇记


    传送门

    看到数据范围,显然可以 $m^3 log n$ 考虑构造矩阵

    考虑 $i^m cdot m^i$ 怎么通过矩阵变成 $(i+1)^m cdot m^{i+1}$

    首先后面那个 $m^i$ 变成 $m^{i+1}$ 十分显然,现在只要考虑 $i^{m}$ 变成 $(i+1)^m$

    把 $(i+1)^m$ 展开,由二项式定理得到 $(i+1)^m=sum_{j=0}^{m}inom{j}{m}i^j$

    那么我们首先维护一个这样的矩阵 $A$:

    $egin{bmatrix}i^0 & i^1 & ... & i^m & end{bmatrix}$

    然后可以构造出转移矩阵 $F$:

    $egin{bmatrix}inom{0}{0} & inom{0}{1} & ... & inom{0}{m-1} & inom{0}{m}\ 0 & inom{1}{1} & ... & inom{1}{m-1} & inom{1}{m}\ 0 & 0 & ... & inom{2}{m-1} & inom{2}{m}\ ... & ... & ... & ... & ...\ 0 & 0 & ... & 0 & inom{m}{m}end{bmatrix}$

    这样 $A$ 乘上 $F$ 以后就能得到 $(i+1)^m$ 了,因为之前还有个 $m^i$ 到 $m^{i+1}$,所以转移矩阵 $F$ 每一项再乘一个 $m$ 即可

    因为答案要记录过程转移中的和,所以矩阵再多一个位置留给 $Ans$ 即可,最终

    $A$ 为

    $egin{bmatrix}i^0 cdot m^i & i^1 cdot m^i & ... & i^m cdot m^i & sum_{j=0}^{i-1}j^m cdot m^j end{bmatrix}$

    $F$ 为

    $egin{bmatrix}inom{0}{0}m & inom{0}{1}m & ... & inom{0}{m-1}m & inom{0}{m}m & 0\ 0 & inom{1}{1}m & ... & inom{1}{m-1}m & inom{1}{m}m & 0\ 0 & 0 & ... & inom{2}{m-1}m & inom{2}{m}m & 0\ ... & ... & ... & ... & ... & ...\ 0 & 0 & ... & inom{m-1}{m-1}m & inom{m-1}{m}m & 1\ 0 & 0 & ... & 0 & inom{m}{m}m & 1end{bmatrix}$

    然后矩阵快速幂即可

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=207,mo=1e9+7;
    inline int fk(int x) { return x>=mo ? x-mo : x; }
    int n,m,C[N][N],T;
    struct Matrix {
        int a[N][N];
        Matrix () { memset(a,0,sizeof(a)); }
        inline Matrix operator * (const Matrix &tmp) const {
            Matrix res;
            for(int i=0;i<=T;i++)
                for(int j=0;j<=T;j++)
                    for(int k=0;k<=T;k++)
                        if(a[i][k]&&tmp.a[k][j])
                            res.a[i][j]=fk(res.a[i][j]+1ll*a[i][k]*tmp.a[k][j]%mo);
            return res;
        }
    }Ans,F;
    inline Matrix ksm(Matrix x,int y)
    {
        Matrix res; for(int i=0;i<=T;i++) res.a[i][i]=1;
        while(y)
        {
            if(y&1) res=res*x;
            x=x*x; y>>=1;
        }
        return res;
    }
    int main()
    {
        n=read(),m=read(); T=m+1;
        for(int i=0;i<=m;i++)
        {
            C[i][0]=1;
            for(int j=1;j<=i;j++) C[i][j]=fk(C[i-1][j-1]+C[i-1][j]);
        }
        Ans.a[0][0]=1;
        for(int i=0;i<=m;i++)
            for(int j=0;j<=i;j++) F.a[j][i]=1ll*m*C[i][j]%mo;
        F.a[T-1][T]=F.a[T][T]=1;
        Ans=Ans*ksm(F,n);
        printf("%d
    ",fk(Ans.a[0][T]+Ans.a[0][T-1]));//注意此时最后一项还没计入a[0][T]
        return 0;
    }

    啥?还有 $m^2$ 的做法?那补一个推导过程好了

    设 $F_i=sum_{k=1}^{n}k^{i}m^{k}$,首先 $F_{0}$ 可以直接等比数列求和得到

    然后

    $mF_i=sum_{k=1}^{n}k^{i}m^{k+1}$

    $(m-1)F_i=mF_i-F_i=sum_{k=1}^{n}k^{i}m^{k+1}-sum_{k=1}^{n}k^{i}m^{k}$

    $=sum_{k=2}^{n+1}(k-1)^{i}m^{k}-sum_{k=1}^{n}k^{i}m^{k}$

    因为前面那个式子当 $k=1$ 时没贡献,所以

    $=sum_{k=1}^{n}(k-1)^{i}m^{k}+n^{i}m^{n+1}-sum_{k=1}^{n}k^{i}m^{k}$

    $=n^{i}m^{n+1}+sum_{k=1}^{n}(k-1)^{i}m^{k}-sum_{k=1}^{n}k^{i}m^{k}$

    $=n^{i}m^{n+1}+sum_{k=1}^{n}m^{k}((k-1)^{i}-k^i)$

    直接把 $(k-1)^i$ 拆开,$(k-1)^i=sum_{j=0}^{i}(-1)^{i-j}inom{j}{i}k^j$,所以

    $=n^{i}m^{n+1}+sum_{k=1}^{n}m^{k}(sum_{j=0}^{i-1}(-1)^{i-j}inom{j}{i}k^j)$

    $=n^{i}m^{n+1}+sum_{j=0}^{i-1}(-1)^{i-j}inom{j}{i}sum_{k=1}^{n}m^{k}k^j$

    发现后面那一团就是 $F_j$ ?

    $=n^{i}m^{n+1}+sum_{j=0}^{i-1}(-1)^{i-j}inom{j}{i}F_j$

    所以直接 $m^2$ 递推就完事了

    啥?还有 $O(m)$ 的做法??????根本不会,溜了

  • 相关阅读:
    file_zilla 通过key连接远程服务器
    git 恢复丢失的文件
    花括号中的json数据--->转为数组array
    3种日志类型,微信付款反馈-->写入txt日志
    清空数据库中所有表--连表删除
    冒泡排序, 使用最低票价.---双重循环,一重移动次数.二重移动
    navicat 连接远程mysql
    付款前.检查状态.防止重复付款,需要ajax设置为同步,等待ajax返回结果再使用
    反射
    设计模式六大原则
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/11506432.html
Copyright © 2020-2023  润新知