HDU 3658 How many words

本文介绍了一种利用矩阵优化技术解决特定组合问题的方法,通过构建递推式和运用矩阵运算,有效地解决了时间复杂度问题。文章详细阐述了算法的原理、实现过程以及时间复杂度分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:

从小写字母和大写字母里选出m个字母(可重复选择),组成一个单词,选字母时需要满足以下规则:

1.相邻字母ASCII值的差值<=32

2.选择的单词中至少有一对字母ASCII值的差值恰好等于32.

问你可以组成多少个不同的单词。

输出最后的结果mod 1000000007

其中:(2 ≤ m ≤ 109)

题解:

如果我们不考虑第二条规则和数据范围,可以推出如下递推式,设f[i][j]表示组成长为i的单词最后一个字母为j且相邻字母ASCII值的差值<=32的方案数,则有:

f[i][j]=sigma(f[i-1][k]),abs(ASCII(j)-ASCII(k))<=32。

为了保证第二条规则,我们再推一个递推式g[i][j]=sigma(g[i-1][k]),abs(ASCII(j)-ASCII(k))<=31,g[i][j]表示组成长为i的单词最后一个字母为j且相邻字母ASCII值的差值<=31的方案数。

那么最后答案便是这两个递推式分别在i=m时分别求和再相减。


但是这个题时间复杂度不够,想到了矩阵优化线性递推式,f[i][j],g[i][j]均是线性的且可以用滚动数组降维,仔细推敲发现可以用矩阵来优化这个递推式,这样这个题就完美的解决了。

时间复杂度O(52*52*52*logm),其中52是构造的矩阵的大小

实现时,为了程序易懂好写,采用了运算符重载。

#include<iostream>
#include<cstdlib>
#include<cstring>
using namespace std;
const long long mod=1000000007;
struct Matrix
{
    long long a[55][55];
    Matrix operator *(Matrix &b)
    {
        Matrix c;memset(c.a,0,sizeof(c.a));
        for(int i=1;i<=52;i++)
         for(int j=1;j<=52;j++)
         {
            for(int k=1;k<=52;k++)
            c.a[i][j]=(c.a[i][j]+(a[i][k]*b.a[k][j])%mod)%mod;
         }
        return c;
    }
    Matrix operator ^(long long x)
    {
        Matrix e,b;
        memset(e.a,0,sizeof(e.a));
        for(int i=1;i<=52;i++)
        e.a[i][i]=1;
        memcpy(b.a,a,sizeof(a));
        while(x!=0)
        {
            if(x&1)e=e*b;
            b=b*b;
            x=x>>1;
        }
        return e;
    }
};
long long m;
int main()
{
    int sec;
    cin>>sec;
    for(int i=1;i<=sec;i++)
    {
        cin>>m;
        Matrix a,b;
        memset(a.a,0,sizeof(a.a));
        memset(b.a,0,sizeof(b.a));
        for(int i=1;i<=52;i++)
        {
            for(int j=max(i-26,1);j<=min(i+26,52);j++)
            a.a[j][i]=1;
        }
        for(int i=1;i<=52;i++)
        {
            for(int j=max(i-25,1);j<=min(i+25,52);j++)
            b.a[j][i]=1;
        }
        a=a^(m-1);
        b=b^(m-1);
        long long ans=0,dec=0;
        for(int i=1;i<=52;i++)
         for(int j=1;j<=52;j++)
        {
            ans=(ans+a.a[i][j])%mod;
            dec=(dec+b.a[i][j])%mod;
        }
        ans=(ans+mod-dec)%mod;
        cout<<ans<<"\n";
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值