[2019牛客国庆集训day1D][dp]

传送门
题意:有一个长度为n的由0~9组成的数字串,其中已知有m个区间的区间乘积%9==0,求满足条件的数字串的个数%(1e9+7)的结果

题解:要想区间乘积是9的倍数,区间内因子3的个数至少为2个,其中0和9相当于2个3,3和6相当于1个3,其余数字相当于0个3,那么可以用dp[i][j][k]表示前i个里面距离i最近的两个3在j和k位置的情况数,如果两个区间的右端点一样,那么显然只要使最小的那个区间满足条件即可,转移过程对于第i个位置,只有k>=L[i-1]时才使用dp[i-1][j][k]更新和i有关的dp值,这样可以保证是使用前i-1个合法情况进行转移的,这时也不需要考虑第i个是否满足>=L[i],因为反正后面i+1使用的也是k>=L[i]的情况。这种通过限定转移过程的dp是很常见的

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=1e5+5;
#define debug(x) cout<<#x<<" is "<<x<<endl;

ll dp[55][55][55];
int l[55];

int main(){
    int n,m;
    while(scanf("%d%d",&n,&m)==2){
        memset(dp,0,sizeof(dp));
        memset(l,0,sizeof(l));
        for(int i=1;i<=m;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            l[b]=max(l[b],a);
        }
        dp[0][0][0]=1;
        for(int i=1;i<=n;i++){
            for(int j=0;j<i;j++){
                for(int k=0;k<=j;k++){
                    if(k>=l[i-1]){
                        dp[i][j][k]=(dp[i][j][k]+6*dp[i-1][j][k])%mod;
                        dp[i][i][j]=(dp[i][i][j]+2*dp[i-1][j][k])%mod;
                        dp[i][i][i]=(dp[i][i][i]+2*dp[i-1][j][k])%mod;
                    }
                }
            }
        }
        ll ans=0;
        for(int i=0;i<=n;i++){
            for(int j=0;j<=i;j++){
                if(j>=l[n]){
                    ans=(ans+dp[n][i][j])%mod;
                }
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值