51nod 1232 完美数 状态压缩+数位dp

题目来源:  胡仁东
基准时间限制:2 秒 空间限制:131072 KB 分值: 160  难度:6级算法题
 收藏
 关注
如果一个数能够被组成它的各个非0数字整除,则称它是完美数。例如:1-9都是完美数,10,11,12,101都是完美数,但是13就不是完美数(因为13不能被数字3整除)。
现在给定正整数x,y,求x和y之间(包含x和y的闭区间)共有多少完美数。

题目作者为: hrdv
Input
第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 10000)
第2 - T + 1行:每行2个数,X, Y中间用空格分割。(1 <= X <= Y <= 10^18)
Output
输出共T行,对应区间中完美数的数量。
Input示例
2
1 9
12 15
Output示例
9

2

/**
求解区间完美数的个数;
完美数:一个数能够整除它的所有非零数位;
小技巧hash : 对于最大的数位 lcm_max=2520; 离散预处理hash值
因此我们在对该数字进行求和过程中 只需要对2520进行取余即可;
dp[len][digit][mp[now_lcm]];
表示第len位digit(前面值和)%2520后所有数位为lcm为now_lcm的个数
如此空间就不是问题了 
*/

#include<bits/stdc++.h>
#define  ll unsigned long long
using namespace std;

ll gcd(ll a,ll b){ return b==0?a:gcd(b,a%b);}

ll dp[19][2522][50];
int num[10],len,mp[2522];

void init(){
	int k=0;
	for(int i=1;i<=2520;i++){
		if(2520%i==0){
			mp[i]=++k;
		}
	}	
} 

ll dfs(int len,int digit,int now_lcm,bool end_flag){
	if(len<=0) return digit%now_lcm==0;
	if(!end_flag&&dp[len][digit][mp[now_lcm]]!=-1) return dp[len][digit][mp[now_lcm]];
	int maxx=end_flag?num[len]:9;
	ll ans=0;
	for(int i=0;i<=maxx;i++)
        ans+=dfs(len-1,(digit*10+i)%2520,i==0?now_lcm:now_lcm/gcd(now_lcm,i)*i,i==maxx&&end_flag);
	return end_flag?ans:dp[len][digit][mp[now_lcm]]=ans;
}

ll solved(ll n){
	len=0;
	while(n){
		num[++len]=n%10;
		n/=10;
	}
	return dfs(len,0,1,true); 
}

int main (){
    
	init();
	int t;
	cin>>t;
	memset(dp,-1,sizeof(dp));
	while(t--){
		
		memset(num,0,sizeof(num));
		ll l,r;
		cin>>l>>r;
		cout<<solved(r)-solved(l-1)<<endl;
	} 
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值