2019 HNCPC D.Modulo Nine(数位dp)

链接

        Modulo Nine

题意

        求长度为n的一个由0~9组成的数字串,满足m个限定,每个限制条件规定[Li,Ri]之间的数字相乘结果%9为0,求不同数字串的个数。

        n<=50,m<=50

思路

        由于可以有前缀0,考虑有区间限制的数位dp。

        两个0~9之间数字相乘%9为0,可能方案数可能为一个0/9,或者有两个3/6.

        当填入的数为0/9时,已满足该区间的限制。当填入的数为3/6时,该区间还要填入一个3/6/0/9.

        自此把数字分成了三类,0/9是第一类,3/6是第二类,其他6个数字是第三类。

        0/9可以看作在这一位同时填入了两个3/6

        dp[i][j][k]表示dp到第i位时,最后的两个使限制条件得以满足的数字的位置为j,k(j>=k)时答案数。

        则有:

        dp[i+1][j][k] += 6\cdot dp[i][j][k]        第i+1位填入1/2/4/5/7/8

        dp[i+1][i+1][j] += 2\cdot dp[i][j][k]        第i+1位填入3/6

        dp[i+1][i+1][i+1] += 2\cdot dp[i][j][k]        第i+1位填入0/9

        其中j和k应满足:

        j\geq L[i]

        k\geq L[i]

        j\geq k

        其中L[i]表示以i为区间右端点的最大左端点(以i为右端点的最小区间的左端点)

        最终只需统计dp[n][j][k]的sum即可得到最终答案。

代码

#include 
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
static const int maxn = 55;
static const int INF = 0x3f3f3f3f;
static const int mod = (int)1e9 + 7;
static const double eps = 1e-6;
static const double pi = acos(-1);

void redirect(){
    #ifdef LOCAL
        freopen("test.txt","r",stdin);
    #endif
}
int L[maxn];
int dp[maxn][maxn][maxn];
int main(){
    redirect();
    int n,m,l,r;
    while(~scanf("%d %d",&n,&m)){
        memset(L,0,sizeof(L));
        memset(dp,0,sizeof(dp));
        for(int i = 1;i <= m;i++){
            scanf("%d %d",&l,&r);
            L[r] = max(L[r],l);
        }
        dp[0][0][0] = 1;
        for(int i = 0;i < n;i++)
        for(int j = L[i];j <= i;j++)
        for(int k = L[i];k <= j;k++){
            dp[i+1][j][k] = (dp[i+1][j][k] + 6ll*dp[i][j][k])%mod;
            dp[i+1][i+1][j] = (dp[i+1][i+1][j] + 2ll*dp[i][j][k])%mod;
            dp[i+1][i+1][i+1] = (dp[i+1][i+1][i+1] + 2ll*dp[i][j][k])%mod;
        }
        int ans = 0;
        for(int i = L[n];i <= n;i++)
        for(int j = L[n];j <= i;j++)
        ans = (ans + dp[n][i][j])%mod;
        printf("%d\n",ans);
    }
    return 0;
}

 

你可能感兴趣的:(动态规划)