1. 程式人生 > >牛客練習賽35-背單詞-線性DP

牛客練習賽35-背單詞-線性DP

背單詞

思路 :dp[ i ]  [ 0 ]表示 第i 位放的母音  dp[ i ]  [ 1 ]表示 第i 位放的子音 ,cnt [ i ]含義是 長度為 i 的方案數。

轉移  :dp[ i ]  [ 0 ]  由 上一個長度的所有方案數 也就是 cnt[ i-1 ]  *5 轉移而來 。

同理   dp[ i ]  [ 1 ]  由 上一個長度的所有方案數 也就是 cnt[ i-1 ]  *21 轉移而來 。

但是 這是 無任何限制的情況下現在加了限制,連續 母音不超過 a  連續 子音不超過 b

那麼  首先 長度得  > a  或 > b 才會出現這種情況。 那麼,當長度i = a +1 時 照常運算dp[ i ]  [ 0 ] ,

但是需要減去  產生了  a + 1個母音相連的情況 。 也就是減去  dp[ i - a - 1][ 1 ] * pa  。pa是連續a+1 個母音的各種組合方案數

 dp[ i - a - 1][ 1 ]  是去掉 a + 1的長度  之前 最後一個字母為子音的狀態。

同理 當長度i = b +1 時 照常運算dp[ i ]  [ 1 ] ,但是需要減去  產生了  b + 1個母音相連的情況 。

也就是減去  dp[ i - b - 1][ 0 ] * pb  。pb是連續b+1 個子音的各種組合方案數

 dp[ i - b - 1][ 0 ]  是去掉 b + 1的長度  之前 最後一個字母為母音的狀態。

不會 出現 a + 2  a + 3.....個母音相連,b+2,b + 3 .....個子音相連 ,因為 a + 1個 母音相連 ,b +1 個子音相連的情況,

都已經去掉了 ,長度一個一個增加不會產生 b + 2 ,a  + 2  後面的也就更不會產生 。

ans 不斷 求和 各個長度下的 方案數。

#include<bits/stdc++.h>
using namespace std;
const int mod=1000000007;
#define ll long long
#define maxn 5678
ll pa,pb,dp[maxn][2],cnt[maxn],ans;
int t,n,a,b;
ll qpow(ll a,ll b)
{
    ll ret=1;
    while(b)
    {
        if(b%2)
            ret=(ret*a)%mod;
        a=(a*a)%mod;
        b/=2;
    }
    return ret;
}
int main()
{
    dp[0][0]=dp[0][1]=1;
    dp[1][0]=5,dp[1][1]=21;
    cnt[1]=26;
    scanf("%d",&t);
    while(t--)
    {
        ans=26;
        scanf("%d%d%d",&n,&a,&b);
        pa=qpow(5,a+1);
        pb=qpow(21,b+1);
        for(int i=2; i<=n; i++)
        {
            dp[i][0]=5*cnt[i-1]%mod;
            dp[i][1]=21*cnt[i-1]%mod;
            if(i>a) dp[i][0]=(dp[i][0]-dp[i-a-1][1]*pa+mod)%mod;
            if(i>b) dp[i][1]=(dp[i][1]-dp[i-b-1][0]*pb+mod)%mod;
            cnt[i]=(dp[i][0]+dp[i][1])%mod;
            ans=(ans+cnt[i]+mod)%mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}