2017 暑假艾教集訓 day5

HDU 2157

做法:做一個矩陣 MU[S][T]=1 ,那麼答案就是 MU[S][T] 在 K次矩陣乘之後的(矩陣快速冪);


 

HDU 821E

做法:和上道題略有區別 分段矩陣快速冪,每次保留最右界的可能數,這裏有個wa點需要把 y+1以上的方案都改爲0,不可到達。 然後構造如下矩陣




#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
struct Matix
{
    ll mu[16][16];
    Matix(){ memset(mu,0,sizeof(mu));}
};

Matix  muilt(Matix a, Matix b,int len)
{
    Matix ans;
    for(int i=0;i<=len;++i)
    {
        for(int j=0;j<=len;++j)
        {
            for(int k=0;k<=len;++k)
            {
                ans.mu[i][k] = (ans.mu[i][k] + a.mu[i][j] * b.mu[j][k])%mod;
            }
        }
    }
    return ans;
}

Matix powmod(Matix bit ,ll n,int len)
{
    Matix ans;
    for(int i=0;i<=len;++i) ans.mu[i][i]=1;
    while(n)
    {
        if( n& 1) ans = muilt(ans,bit,len);
        bit= muilt(bit,bit,len);
        n>>=1;
    }
    return ans;
}
int main()
{
    int n;  ll aim;
    scanf("%d%I64d",&n,&aim);

    Matix a;
    a.mu[0][0]=1; a.mu[0][1]=1;
    for(int i=1;i<15;++i)
    {
        a.mu[i][i]=1; a.mu[i][i+1]=1; a.mu[i][i-1]=1;
    }
    a.mu[15][14]=1;  a.mu[15][15]=1;

    Matix ans;
    ans.mu[0][0]=1;
    while(n--)
    {
        bool flag=0;
        ll l,r;
        int y;
        scanf("%I64d%I64d%d",&l,&r,&y);
        if(flag==1) continue;
        if(r>=aim)  { r=aim; flag=1;}

        Matix temp = powmod(a ,(r-l),y);
        for(int i=y+1;i<16;++i) ans.mu[i][0]=0;

        temp = muilt(temp ,ans ,y);
        for(int i=0;i<=y;++i) ans.mu[i][0] = temp.mu[i][0];
    }
    printf("%I64d\n",ans.mu[0][0]);
    return 0;
}

HDU 5863


n的規模達到了10億,而且又是方案數,自然就想到構造矩陣用快速冪解決。
考慮用DP解決可以這麼表示狀態:
dp[i][j]表示兩個字符串前i個字符都構造好了 並且 它們後面的j個字符相同的方案數
狀態的轉移就是,末尾j個相同的可以轉移到0個相同的也能轉移到j+1個相同的(前提是j<m)。

而對於這個狀態可以構造矩陣去轉移,即一個(m+1)*(m+1)的矩陣,矩陣i行j列表示從末尾i個相同轉移到末尾j個相同的方案數,而該矩陣的n次冪的第0行的和就是長度n的字符串末尾各個情況的方案數。
不過樣表示狀態最後求出來不是要求的,因爲LCS小於m的也會包含於其中。那麼減去小於m的方案數不就OK了!
即 至少包含m個相同公共子串的方案數 - 至少包含m-1個相同公共子串的方案數 = 恰好包含m個相同公共子串的方案數
於是,一樣再構造一個m*m的矩陣求n次冪,就OK了


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
struct Matrix
{
    ll mu[11][11];
    Matrix()
    {
        memset(mu,0,sizeof(mu));
    }
};
Matrix muilt(Matrix a, Matrix b)
{
    Matrix ans;
    for(int i=0;i<11;++i)
    {
        for(int j=0;j<11;++j)
        {
            for(int k=0;k<11;++k)
            {
                ans.mu[i][k]=(ans.mu[i][k] + a.mu[i][j] *b.mu[j][k]%mod)%mod;
            }
        }
    }
    return ans;
}
Matrix pow(Matrix bit ,ll n)
{
    Matrix ans;
    for(int i=0;i<11;++i) ans.mu[i][i]=1;
    while(n)
    {
        if(n & 1)  ans = muilt(ans,bit);
        bit = muilt(bit,bit);
        n>>=1;
    }
    return ans;
}
ll work(ll n,int  m,int type)
{
    Matrix bit;
    for(int i=0;i<(m+1);++i) bit.mu[0][i]= (type-1)*type;
    for(int i=1;i<(m+1);++i) bit.mu[i][i-1] = type;
    Matrix ans;
    ans = pow(bit,n);
    ll sum=0;
    for(int i=0;i<(m+1);++i)
    {
        sum = (sum + ans.mu[i][0])%mod;
    }
    return sum;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        ll n;
        int m ,k;
        scanf("%I64d%d%d",&n,&m,&k);
        ll ans=work(n,m,k)-work(n,m-1,k);
        printf("%I64d\n", (ans%mod+mod)%mod );
    }
    return 0;
}

第一類斯特林數

假設已經推出了n個元素分成k個環的方法數以及n個元素分成k-1個環的元素,我們考慮第n+1個元素,一種情況是這個元素自己成環,於是是S(n,k-1),另一種情況是把這個元素放到任意一個元素的左邊,方案數是nS(n,k)


第二類斯特林數

考慮第n個元素,如果它自成一個集合,那麼前n-1個元素構成k-1個集合,就是S(n,k),如果它不是自成集合,那麼前面n-1個元素構成k個集合,再把第n個元素加到任意一個集合中,共k種方案。

HDU  3625

做法:

若不考慮第一個房間不允許破門,能全部開門的方案數就是第一類斯特林數S(n,k),但是如果第一個房間的鑰匙在第一個房間中的時候是不能完成目的的,這時剩下的n-1個房間中有k-1個環,必須減去。因此答案爲(S(n,k)-S(n-1,k-1))/n! 

注意 這裏還要對k枚舉,小於k的情況也可以



HDU 4372

做法:

首先最高的那個樓房,無論從左還是從右都能看到,那麼我們固定這個樓房,那麼把最高樓房左邊的分成f-1個組,右邊分成b-1組,並且左邊的每一組是任意排列(圓排列),且高度最高的在最左。這f-1個組的順序關於每組最高的那個樓從左到右是遞增的。同理右邊高度最高的在最右。

因此等價於把n-1個元素分成f-1+b-1個環的方案數再乘以把這f-1+b-1個環選擇f-1個放在左邊的方案數。


HDU 2643

做法: 第二類斯特林數求前綴和,即就是貝爾數!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章