1. 程式人生 > >kuangbin帶你飛 - 專題十五 - 數位dp

kuangbin帶你飛 - 專題十五 - 數位dp

還要 pri splay ems closed 道理 ans 位數 alt

https://vjudge.net/contest/70324

A - Beautiful numbers

統計區間內的,被數位上各個為零數字整除的數的個數。

下面是暴力的數位dp寫法,絕對會TLE的,因為這個要深入到每個數字的最後才能判斷是否合法。因為記憶化的意義在詢問不多的時候用處不大就去掉了。果然2400分的題不能這麽暴力。

技術分享圖片
#include<bits/stdc++.h>
using namespace std;
#define ll long long

int a[20];
int d[10];
ll dfs(int pos,ll sum,bool lead,bool
limit) { //遞歸邊界,最低位是0,那麽pos==-1說明這個數枚舉完了 if(pos==-1) { for(int i=1; i<=9; i++) { if(d[i]) { if(sum%i) return 0; } } return 1; } int up=limit?a[pos]:9;//根據limit判斷枚舉的上界up ll ans=0; //開始計數
for(int i=0; i<=up; i++) { //枚舉,然後把不同情況的個數加到ans就可以了 //合法的狀態向下搜索 if(i) d[i]++; ans+=dfs(pos-1,sum*10ll+i,lead && i==0,limit && i==a[pos]);//最後兩個變量傳參都是這樣寫的 if(i) d[i]--; } return ans; } ll solve(ll x) { //可能需要特殊處理0或者-1
if(x<=0) return 1; int pos=0; while(x) { //把數位分解 a[pos++]=x%10;//編號為[0,pos),註意數位邊界 x/=10; } memset(d,0,sizeof(d)); return dfs(pos-1,0,true,true);//剛開始最高位都是有限制並且有前導零的,顯然比最高位還要高的一位視為0嘛 } int main() { int t; scanf("%d",&t); ll le,ri; while(t--) { scanf("%lld%lld",&le,&ri); printf("%lld\n",solve(ri)-solve(le-1)); } }
View Code

沒想出來怎麽解決,去查了題解,題解暗示說,這樣是和最小公倍數有關的。好像的確很有道理,細節只能自己想了。

首先考慮1~9的最小公倍數,也就是 $1*2^3*3^2*5*7=2520$ ,題解提到一個充要條件,就是一個數假如要能被某些數整除,等價於被這些數的最小公倍數整除,這個充要條件的正確性可以由質因數分解得知,就是說這個數的質因數分解必須比他的各個數位的質因數分解“高”,也就比各個數位的質因數分解的“輪廓”也就是最小公倍數“高”。(註: $lcm(a,b)=\frac{a*b}{gcd(a,b)}$ ,且滿足結合律)

然後怎麽計數呢?這裏受到之前做的數位dp的啟發,由下一位的數位dp推出上一位的狀態。設計狀態的時候借鑒別人的思路,dp[i][j][k]表示i位數中最小公倍數為j且模2520的余數為k的數的個數。

那麽dp[i][j][k]+=dp[i-1][j

kuangbin帶你飛 - 專題十五 - 數位dp