【一天一DP计划】数位dp

入坑之好博

一本通 数位DP

浅谈数位DP

## P4127 同类分布

现在关键的问题是:怎样记录dp状态?

这里 st可达到 1e18 显然是不能作为dp转移的下标直接记录的

所以我们考虑取模

我们最理想的模数当然是把每次搜到最后得到的数字各个位数之和

但是我们发现在这个过程中 sum是发生变化的

所以我们就应该以一个定值作为模数

那好,我们虽然不知道最后各位之和的结果,我们枚举总可以吧

我们只需要枚举所有的各位数字之和作为模数

最后判断 sum 和枚举的 mod相等并且 st%sum=0 的数就是符合题意的答案

/*
reference:
	https://www.luogu.org/blog/virus2017/p4127#
date:
	2019.10.03
solution:
	- 既然st的范围是1e18那么long long都存不下肯定不能记为dp的状态
		- 那么肯定要取模!模数为什么呢 ?最好为当前模数和sum
			- 可是sum在转移的时候一直在改变,那么之前记录的dp状态就无效了!
				- 那我们换个思路,模数呢最大为18*9,那我们枚举每个模数,判断当前在这个模数意义下是否合法
					- 合法的条件:sum==mod && st==0(st要一直%mod) 
*/
int a,b,len,mod; 
int dp[18+5][18*9+5][18*9+5],bit[18+5];

inline int dfs(int pos,int sum,int st,int limit){
	if(pos>len && sum==0)return 0; 
	if(pos>len)return sum==mod && st==0;
	if(!limit && dp[pos][sum][st]!=-1)return dp[pos][sum][st];
	int high=limit?bit[len-pos+1]:9;
	int res=0;
	rep(i,0,high){
		res+=dfs(pos+1,sum+i,(10ll*st+i)%mod,i==high && limit);
	}
//	return limit?res:dp[pos][sum][st]=res;
	if(!limit)dp[pos][sum][st]=res;
	return res;
}

inline int work(int x){
	len=0;
	while(x){
		bit[++len]=x%10;
		x/=10;
	}
	int res=0;
	for(mod=1;mod<=len*9;++mod){
		mem(dp,-1);
		res+=dfs(1,0,0,1);
	}
	return res;
}

#undef int
int main(){
#define int long long
	freopen("tonglei.txt","r",stdin);
	rd(a),rd(b);
	printf("%lld",work(b)-work(a-1)); 
	return 0;
}

##windy数

/*
reference:
	https://www.luogu.org/blog/virus2017/solution-p2657
date:
	2019.10.03
solution:
	最妙:考虑前导零的时候,第一位的时候,0和1是没办法取到的,因为abs(i-pre)<2了,
	那么,就等价于,pre设为-2问题解决 
*/
int a,b,len;
int bit[15],dp[15][15];

inline int dfs(int pos,int pre,int lead,int limit){
	if(pos>len)return 1;
	if(!limit && dp[pos][pre]!=-1)return dp[pos][pre];
	int high=limit?bit[len-pos+1]:9;
	int res=0;
	rep(i,0,high){
		if(abs(i-pre)<2)continue;
		else if(lead && i==0)
			res+=dfs(pos+1,-2,1,i==high && limit);
		else
			res+=dfs(pos+1,i,0,i==high && limit);
	}
	if(!limit && !lead)dp[pos][pre]=res;
	return res;
}

inline int work(int x){
	len=0;
	while(x){
		bit[++len]=x%10;
		x/=10;
	}
	mem(dp,-1);
	return dfs(1,-2,1,1);
}

#undef int
int main(){
#define int long long
	#ifdef WIN64
	freopen("windy.txt","r",stdin);
	#endif
	rd(a),rd(b);
	printf("%lld\n",work(b)-work(a-1));
	return 0;
}

##数字计数(求不降数)

/*
int a,b,len,mod;
int bit[15],dp[15][15];

inline int dfs(int pos,int pre,int limit){
	if(pos>len)return 1;
	if(dp[pos][pre]!=-1 && !limit)return dp[pos][pre];
	int high=limit?bit[len-pos+1]:9;
	int res=0;
	rep(i,pre,high){
		res+=dfs(pos+1,i,i==high && limit);
	}
	if(!limit)dp[pos][pre]=res;
	return res;
}

inline int work(int x){
	len=0;
	while(x){
		bit[++len]=x%10;
		x/=10;
	}
	mem(dp,-1);
	return dfs(1,0,1);
} 
#undef int
int main(){
#define int long long
	#ifdef WIN64
	freopen("shuzi2.txt","r",stdin);
	#endif
	while(~scanf("%lld%lld",&a,&b)){
		printf("%lld\n",work(b)-work(a-1));
	}
	return 0;
}

##不要62

/*
int a,b,len;
int bit[10],dp[10][15];

inline int dfs(int pos,int pre,int limit){
	if(pos>len)return 1;
	if(dp[pos][pre]!=-1 && !limit)return dp[pos][pre];
	int high=limit?bit[len-pos+1]:9;
	int res=0;
	rep(i,0,high){
		if(i==4)continue;
		if(pre==6 && i==2)continue;
		res+=dfs(pos+1,i,i==high && limit);
	}
	if(!limit)dp[pos][pre]=res;
	return res;
}

inline int work(int x){
	mem(bit,0);
	len=0;
	while(x){
		bit[++len]=x%10;
		x/=10;
	}
	mem(dp,-1);
	return dfs(1,0,1);
}

#undef int
int main(){
#define int long long
	#ifdef WIN64
	freopen("62.txt","r",stdin);
	#endif
	while(1){
		rd(a),rd(b);
		if(a==0 && b==0)break; 
		printf("%lld\n",work(b)-work(a-1));
	}
	return 0;
}

##数字游戏(取模)

int a,b,len,mod;
int bit[15],dp[15][900];

inline int dfs(int pos,int sum,int limit){
	if(pos>len)return sum==0?1:0;
	if(!limit && dp[pos][sum]!=-1)return dp[pos][sum];
	int high=limit?bit[len-pos+1]:9;
	int res=0;
	rep(i,0,high){
		res+=dfs(pos+1,(sum+i)%mod,i==high && limit);
	}
	if(!limit)dp[pos][sum]=res;
	return res;
}

inline int work(int x){
	len=0;
	while(x){
		bit[++len]=x%10;
		x/=10;
	}
	mem(dp,-1);
	return dfs(1,0,1);
}

#undef int
int main(){
#define int long long
	#ifdef WIN64
	freopen("shuzi.txt","r",stdin);
	#endif
	while(~scanf("%lld%lld%lld",&a,&b,&mod)){
		printf("%lld\n",work(b)-work(a-1));
	}
	return 0;
}
posted @ 2019-10-08 11:53  设计涉及社稷  阅读(156)  评论(0编辑  收藏  举报