HDU 2294解題報告

Pendant

Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 661    Accepted Submission(s): 331


Problem Description
On Saint Valentine's Day, Alex imagined to present a special pendant to his girl friend made by K kind of pearls. The pendant is actually a string of pearls, and its length is defined as the number of pearls in it. As is known to all, Alex is very rich, and he has N pearls of each kind. Pendant can be told apart according to permutation of its pearls. Now he wants to know how many kind of pendant can he made, with length between 1 and N. Of course, to show his wealth, every kind of pendant must be made of K pearls.
Output the answer taken modulo 1234567891.
 

Input
The input consists of multiple test cases. The first line contains an integer T indicating the number of test cases. Each case is on one line, consisting of two integers N and K, separated by one space.
Technical Specification

1 ≤ T ≤ 10
1 ≤ N ≤ 1,000,000,000
1 ≤ K ≤ 30
 

Output
Output the answer on one line for each test case.
 

Sample Input
2 2 1 3 2
 

Sample Output
2 8
 

Source
 

Recommend
lcy   |   We have carefully selected several similar problems for you:  3117 2256 2842 3519 3524 

         這道題是一道比較經典的矩陣優化DP的題,非常經典,是很值得鑽研的一道題。

         這道題的意思是給你k種珍珠,用k種珍珠串成長度爲1,2~n的方法數之和。

         我們可以用dp[i][j]表示用j中珍珠構成長度爲i的項鍊的方法總數。則我們考慮長度爲i-1的項鍊的情況,此時我們如果用j種珍珠構成了長度爲i-1的項鍊,那麼用j種珍珠構成長度爲i的項鍊時,需要再對長度爲i-1的項鍊再加上的珍珠是j種珍珠之中一種,所以此時構成長度爲i的項鍊共有dp[i-1][j]*j種,當然還有一種情況是用j-1種珍珠構成長度爲i-1的項鍊,那麼要用j中珍珠構成長度爲i的項鍊,就只需要給長度爲i-1的項鍊再添加上剩下(k-j+1)種中的一種,所以此時構成長度爲i的項鍊有dp[i-1][j-1]*(k-j+1)。 

       因此可以寫出狀態轉移方程,dp[i][j]=dp[i-1][j]*j+dp[i-1][j-1]*(k-j+1)。

       根據這個狀態轉移方程以及n的數量級,我們就要想到用矩陣快速冪來加速dp的過程。但是進行矩陣快速冪時我們需要求的值是dp[k][1]+dp[k][2]+……+dp[k][n]的值。如果在構造矩陣時只是對單個dp的值進行記錄的話,那麼就勢必要對矩陣多項式進行求解。但是如果採用二分矩陣多項式進行遞歸求解容易導致遞歸次數太多,出現棧溢出。因而要在矩陣快速冪時就計算dp之和的值,這樣只用一次矩陣快速冪即可求出所有dp的和。

       因此在做和矩陣相關的題的時候就需要格外注意,如果可以一次性通過矩陣快速冪求出某個式子之和,那麼就儘可能不要去使用二分矩陣多項式。因此要構造性能更優的矩陣,來降低時間複雜度。這一點需要好好體會纔行。如何構造矩陣還是很值得思考的知識點。

         參考代碼:

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<ctime>
#include<cstdlib>
#include<iomanip>
#include<utility>
#define pb push_back
#define mp make_pair
#define CLR(x) memset(x,0,sizeof(x))
#define _CLR(x) memset(x,-1,sizeof(x))
#define REP(i,n) for(int i=0;i<n;i++)
#define Debug(x) cout<<#x<<"="<<x<<" "<<endl
#define REP(i,l,r) for(int i=l;i<=r;i++)
#define rep(i,l,r) for(int i=l;i<r;i++)
#define RREP(i,l,r) for(int i=l;i>=r;i--)
#define rrep(i,l,r) for(int i=1;i>r;i--)
#define read(x) scanf("%d",&x)
#define put(x) printf("%d\n",x)
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<11
using namespace std;

const int mod=1234567891;

struct mat
{
    ll d[35][35];
}A,B,E;

int t,n,k;

mat multi(const mat &a,const mat &b)
{
    mat ans;
    REP(i,0,k+1)
    {
        REP(j,0,k+1)
        {
            ans.d[i][j]=0;
            REP(l,0,k+1)
            {
                if(a.d[i][l]&&b.d[l][j])
                    ans.d[i][j]=(ans.d[i][j]+a.d[i][l]*b.d[l][j])%mod;
            }
        }
    }
    return ans;
}

mat quickmulti(mat a,int n)
{
    if(n==0) return E;
    if(n==1) return a;
    mat ans=E;
    while(n)
    {
        if(n&1)
        {
            n--;
            ans=multi(ans,a);
        }
        else
        {
            n>>=1;
            a=multi(a,a);
        }
    }
    return ans;
}

int main()
{
   CLR(E.d);
   REP(i,0,32)
     E.d[i][i]=1;
   read(t);
   while(t--)
   {
       read(n);read(k);
       CLR(A.d);CLR(B.d);
       A.d[k+1][k+1]=1,A.d[k+1][k-1]=1,A.d[k+1][k]=k; //這幾個值是爲了最終就算出所有的dp之和
       REP(i,1,k)
           A.d[i][i-1]=k-i+1,A.d[i][i]=i;
       B.d[1][0]=k;B.d[k+1][0]=0;
       if(k==1) B.d[k+1][0]=1;           //B.d[k+1][0]這個記錄的是dp[k][1]的值,這也是最初始狀態的值。但是當k==1時,dp[1][0]=1,所以這個情況比較特殊
       mat ans=quickmulti(A,n-1);
       ans=multi(ans,B);
       printf("%I64d\n",ans.d[k+1][0]);
   }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章