zoukankan      html  css  js  c++  java
  • 卡牌

    有m种纸牌点数分别为1~m,每种有无穷多张。
    有n个人站成一列,给每个人发一张牌。求满足任意相邻两人牌的点数不为k的分发方案有多少。答案对1e9+7取模。

    分类讨论,fif_i表示前i个人的方案数,aia_i表示第i张牌满足点数<k时的方案数,bib_i表示第i张牌满足>=k时的方案数。显然有fi=ai+bif_i=a_i+b_i
    讨论m与k的关系(很关键!),

    当m+1>k时,
    ai=(k2)ai1+(k1)bi1a_i=(k-2)a_{i-1}+(k-1)b_{i-1} (a),
    bi=(mk+1)(ai1+bi1)b_i=(m-k+1)(a_{i-1}+b_{i-1})(b)。

    由 (a) 得,bi1=ai(k2)ai1k1b_{i-1}=frac{a_i-(k-2)a_{i-1}}{k-1},即 bi=ai+1(k2)aik1b_{i}=frac{a_{i+1}-(k-2)a_{i}}{k-1}
    将 (b) 与上式联立,
    bi=ai+1(k2)aik1=(mk+1)(ai1+bi1)b_{i}=frac{a_{i+1}-(k-2)a_{i}}{k-1}=(m-k+1)(a_{i-1}+b_{i-1})
    bi=ai+1(k2)aik1=(mk+1)(ai1+ai(k2)ai1k1)b_{i}=frac{a_{i+1}-(k-2)a_{i}}{k-1}=(m-k+1)(a_{i-1}+frac{a_i-(k-2)a_{i-1}}{k-1})
    化简得 ai+1=(m1)ai+(mk+1)ai1a_{i+1}=(m-1)a_i+(m-k+1)a_{i-1}
    aa 的递推式已求得。

    bib_iaa 表达的式子代入 fi=ai+bif_i=a_i+b_ifi=ai+1+aik1f_i=frac{a_{i+1}+a_i}{k-1}

    所以用矩阵加速求出 an+1a_{n+1}ana_n,直接代入 fif_i 即可,注意要求k-1的逆元。
    矩阵为{{m-1,1}{m-k+1,0}}。

    当m+1<=k时,m-k+1是k-m-1。

    代码如下 (由于矩阵很小我就懒得写循环做矩阵乘法了哈哈哈):

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int MOD=1e9+7;
    
    long long x[2],ans[2],cf[2][2],tmp[2][2];
    
    void mult_ans()
    {
        ans[0]=((x[0]*cf[0][0])%MOD+(x[1]*cf[1][0])%MOD)%MOD;
        ans[1]=((x[0]*cf[0][1])%MOD+(x[1]*cf[1][1])%MOD)%MOD;
        x[0]=ans[0],x[1]=ans[1];
    }
    
    void mult_cf()
    {
        tmp[0][0]=cf[0][0],tmp[0][1]=cf[0][1],tmp[1][1]=cf[1][1],tmp[1][0]=cf[1][0];
        cf[0][0]=((tmp[0][0]*tmp[0][0])%MOD+(tmp[1][0]*tmp[0][1])%MOD)%MOD;
        cf[0][1]=((tmp[0][0]*tmp[0][1])%MOD+(tmp[0][1]*tmp[1][1])%MOD)%MOD;
        cf[1][0]=((tmp[1][0]*tmp[0][0])%MOD+(tmp[1][1]*tmp[1][0])%MOD)%MOD;
        cf[1][1]=((tmp[1][0]*tmp[0][1])%MOD+(tmp[1][1]*tmp[1][1])%MOD)%MOD;
    }
    
    long long ksm(long long a,long long b)
    {
        long long ret=1;
        while(b)
        {
            if(b&1)
       	    ret=(ret*a)%MOD;
            a=(a*a)%MOD;
            b>>=1;
        }
        return ret;
    }
    
    int main()
    {
        long long n,m,k;
        cin>>m>>n>>k;
        cf[0][0]=(m-1),cf[0][1]=1,cf[1][0]=abs(m-k+1),cf[1][1]=0;
        x[0]=(k-1)%MOD*(m-1)%MOD,x[1]=(k-1);
        n--;
        while(n)
        {
            if(n&1)
                mult_ans();
            n/=2;
            mult_cf();
        }
        cout<<(ans[1]+ans[0])%MOD*ksm(k-1,MOD-2)%MOD;
        return 0;
    }
    
  • 相关阅读:
    hdu 3333 树状数组+离线处理
    poj 2352 树状数组 OR Treap
    hdu 1698 线段树
    【概率dp】D. Card Collector
    【分段哈希】H. Paint the Wall
    【置换】G. Poker 2.0
    【概率dp】C. Race to 1 Again
    【dp】D. Caesar's Legions
    【并查集】F.find the most comfortable road
    【算法系列学习】连续邮资问题
  • 原文地址:https://www.cnblogs.com/InedibleKonjac/p/12654961.html
Copyright © 2011-2022 走看看