链接
题意
求长度为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)时答案数。
则有:
第i+1位填入1/2/4/5/7/8
第i+1位填入3/6
第i+1位填入0/9
其中j和k应满足:
其中L[i]表示以i为区间右端点的最大左端点(以i为右端点的最小区间的左端点)
最终只需统计dp[n][j][k]的sum即可得到最终答案。
代码
#include <bits/stdc++.h>
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;
}