鏈接
題意
求長度爲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;
}