2019 HNCPC D Modulo Nine 数位DP

题意

给定长度n和m个限制(l,r),每一个限制的意义是(a_l imes a_{l+1} imes ... a_r = 0 quad mod 9)

问有多少个满足所有条件且长度为n的10进制数(可以包含前导0)

解题思路

看到可以包含前导零的时候就想到了数位DP,但是比赛的时候没想出怎么记录状态来记忆化搜索。赛后听ljn讲了之后秒懂,我还是太菜了。

模9为0可以分为3种情况:区间内9的个数大于等于1,0的个数大于等于1,或,3的个数+6的个数大于等于2,所以可以用4维数组(dp_{pos,last09,last36,second36})来记录状态

(dp_{pos,last09,last36,second36}):pos表示当前枚举到了第几位,last09表示前一个0或9的下标,last36表示前一个3或6的下标,second36表示last36往前一个3或6的下标。

然后按当前位为0或9,3或6,其他数字分类讨论,如果满足以pos为右端点的所有限制就继续转移。

代码实现

和叉姐的标称对拍没有出错,应该是对的吧

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn=1e5+5;
const int mod=1e9+7;

int n,m;
int dp[55][55][55][55];//pos 09 36-1 36-2 
vector<int>limit[55];
ll dfs(int pos,int last_09,int last_36,int second_36){
	if(pos==n+1)return 1;
	if(dp[pos][last_09][last_36][second_36]!=-1)return dp[pos][last_09][last_36][second_36];
	ll res=0;
	for(int i=0;i<=9;i++){//枚举当前位
		if(i==0 || i==9){
			res=(res+dfs(pos+1,pos,last_36,second_36))%mod;
		}
		else if(i==3 || i==6){
			bool flag=true; 
			for(int l:limit[pos]){
				if(last_09>=l)continue;
				if(last_36>=l)continue;
				flag=false;
			}
			if(flag)res=(res+dfs(pos+1,last_09,pos,last_36))%mod;
		}
		else{
			bool flag=true;
			for(int l:limit[pos]){
				if(last_09>=l)continue;
				if(second_36>=l)continue;
				flag=false;
			}
			if(flag)res=(res+dfs(pos+1,last_09,last_36,second_36))%mod;
		}
	}
	dp[pos][last_09][last_36][second_36]=res;
	return res;
}
ll f(){
	memset(dp,-1,sizeof(dp));
	return dfs(1,-1,-1,-1);
}
int main()
{
	while(~scanf("%d %d",&n,&m)){
		int l,r;
		for(int i=1;i<=n;i++)limit[i].clear();
		for(int i=1;i<=m;i++){
			scanf("%d %d",&l,&r);
			limit[r].push_back(l);
		}
		printf("%lld
",f());
	}
    return 0;
}

原文地址:https://www.cnblogs.com/zengzk/p/11445166.html