1. 程式人生 > >Codeforces 55D Beautiful numbers(數位dp)

Codeforces 55D Beautiful numbers(數位dp)

pac urn etc number div clu 能夠 是我 tdi

  題目大意:T(<=10)組數據,求[a,b]能夠被其每個數位的數都整除的數(a,b<=9*10^18)

  這題差一點就想出來了,可是最後一步好難想也好妙啊

  首先這個數能夠整除各個數位的lcm,而最大的lcm為2520(5*7*8*9)。我的想法是枚舉lcm(記為lcmm),求出各個數位的lcm為lcmm且這個數能整除lcm的數有多少,dp[pos][val][lcm][lcmm]表示枚舉lcmm,前pos位,這個數%lcmm為val,各個數位的lcm,然後實際上lcmm和lcm只有50個,所以後兩維離散化就從2520->50了。但是我突然眉頭一皺發現事情並不簡單,pos最大為20,val最大為2520,lcm最大為50,lcmm最大為50,20*2520*50*50=126000000... QAQ滾去看題解

  設這個數為a,各數位為ai,lcm(ai)|a,lcm(ai)|2520,所以lcm(ai)|(a%2520)。這個怎麽證呢?其實很簡單。

  設lcm(ai)x=a,lcm(ai)y=2520,則(a%2520)=lcm(ai)x-lcm(ai)y*z=lcm(ai)(x-y*z),還是含有lcm(ai)。也就是說並不需要枚舉lcmm,只要邊dp邊把val%2520就可以了...省了一維50,於是就巧妙的過了。

代碼如下:

技術分享
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#define
ll long long using namespace std; ll dp[20][2550][50],a[20],l,r,num[2550],cnt,T; void read(ll &k) { k=0;ll f=1;char c=getchar(); while(c<0||c>9)c==-&&(f=-1),c=getchar(); while(c<=9&&c>=0)k=k*10+c-0,c=getchar(); k*=f; } ll gcd(ll a,ll b){return b?gcd(b,a%b):a;} ll dfs(
int pos,int val,int lcm,bool limit) { if(pos==0)return val%lcm==0; if(!limit && dp[pos][val][num[lcm]]!=-1)return dp[pos][val][num[lcm]]; int up=limit?a[pos]:9; ll ans=0; for(int i=0;i<=up;i++) ans+=dfs(pos-1,(val*10+i)%2520,i?(ll)lcm*i/gcd(lcm,i):lcm,limit && i==a[pos]); if(!limit)dp[pos][val][num[lcm]]=ans; return ans; } ll solve(ll x) { if(x==-1)return 0; int pos=0; while(x) { a[++pos]=x%10; x/=10; } ll ans=0; ans+=dfs(pos,0,1,1); return ans; } int main() { for(int i=1;i<=2520;i++) if(!(2520%i))num[i]=++cnt; memset(dp,-1,sizeof(dp)); read(T); while(T--)read(l),read(r),printf("%lld\n",solve(r)-solve(l-1)); }
View Code

Codeforces 55D Beautiful numbers(數位dp)