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

做法: 第二类斯特林数求前缀和,即就是贝尔数!

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