[AHOI2009]同类分布(数位dp)

给出两个数 \(a,b\),求出 \([a,b]\) 中各位数字之和能整除原数的数的个数。\(1\le a,b\le {10}^{18}\)

看起来就很像数位 dp,但是不知道要加哪些状态。我们逐步考虑:\(len,lim\) 肯定要有。要求“整除原数”,每一位的和 \(sum\) 和原数 \(num\) 似乎不错。\(sum\) 最大不会超过 \(18\times 9=162\) ,问题是,\(num\)\(10^{18}\) 量级的。我们不可能直接放在状态里。

不难想到,取模可以缩小 \(num\) 范围。模数是什么?最理想的当然是 \(sum\),如果是取模完是 \(0\) 就说明整除了。然而,在搜索过程里,\(sum\) 是变化的,模数应当是一个定值。怎么办?我们大可以枚举 \(sum\) 的所有可能结果作为模数。这不会对时间带来很大压力。

这道题从数位 dp 死板的框架下突破出来,设计了巧妙的问题,引发思考。

下面是数位 dp 主体部分:

LL dfs(ri len,ri lim,ri sum,register LL num){
	if(len==0){
		if(sum==0) return 0;
		else return sum==mod&&num==0;
	}
	if(lim==0&&f[len][sum][num]!=-1){
		return f[len][sum][num];
	}
	ri up=lim?a[len]:9;
	register LL ans=0;
	for(ri i=0;i<=up;++i){
		ans+=dfs(len-1,lim&&i==up,sum+i,(10ll*num+i)%mod);
	}
	if(!lim) f[len][sum][num]=ans;
	return ans;
}

下面是 calc() 函数,枚举了模数 \(mod\)

inline LL calc(LL x){
	ri len=0;
	while(x){
		a[++len]=x%10;
		x/=10;
	}
	register LL ans=0;
	for(mod=1;mod<=len*9;++mod){
		memset(f,-1,sizeof f);
		ans+=dfs(len,1,0,0);
	}
	return ans;
}

THE END

posted @ 2021-09-22 23:32  q0000000  阅读(38)  评论(0编辑  收藏  举报