【BZOJ 2713】[Violet 2]愚蠢的副官&&【BZOJ1183】[Croatian2008]Umnozak——【数位DP】
题目链接:
题解:
由于看不懂英文题解(十个单词十一个不认识……),所以只能自己想QAQ。
其实乱搞就好= =。
首先我们发现,各位数字乘积要在1e9以下才可能有用,这个很显然。然后发现在1e9以内的满足为各位数字乘积的数只有$5000^+$。我们仿照淘金拉道题的思路。设$f_{(i,j,k)}$表示位数为$i$,第i位数字为$j$,各位数字乘积在$Hash$表里排名为k,转移很好写。然后对于询问的$x$,考虑在$x/Hash_{(k)}$中有多少个满足乘积为$Hash_{(k)}$的即可。
值得注意的是由于我的码的问题,1应该特判掉。
代码:
1 #define Troy 2 3 #include <algorithm> 4 #include <cstdio> 5 6 using namespace std; 7 8 inline int read(){ 9 int s=0,k=1;char ch=getchar(); 10 while(ch<'0'|ch>'9') ch=='-'?k=-1:0,ch=getchar(); 11 while(ch>47&ch<='9') s=s*10+(ch^48),ch=getchar(); 12 return s*k; 13 } 14 15 typedef long long ll; 16 17 const int N=6e3; 18 19 ll Hash[N];int cnt=0; 20 ll f[22][22][N]; 21 22 inline int find(ll x){ 23 return upper_bound(Hash+1,Hash+cnt+1,x)-Hash-1; 24 } 25 26 inline void init(){ 27 for(ll x=1;x<=1e9;x*=2) 28 for(ll y=x;y<=1e9;y*=3) 29 for(ll z=y;z<=1e9;z*=5) 30 for(ll w=z;w<=1e9;w*=7) 31 Hash[++cnt]=w; //cnt=5194 32 sort(Hash+1,Hash+cnt+1); 33 for(int i=1;i<=9;i++) 34 f[1][i][i]=1; 35 for(int i=2;i<=18;i++) 36 for(int k=1;k<=cnt;k++) 37 for(int j=1;j<=9;j++) 38 if(Hash[k]%j==0){ 39 int x=find(Hash[k]/j); 40 for(int l=1;l<=9;l++) 41 f[i][j][k]+=f[i-1][l][x]; 42 } 43 } 44 45 int p[20],m; 46 47 inline ll solve(ll x,int pos){ 48 if(x==0) return 0; 49 ll t=1; 50 int tt=pos; 51 m=0;while(x) p[++m]=x%10,x/=10,t*=p[m]; 52 ll ret=0; 53 if(t==Hash[pos]) ret++; 54 for(int i=1;i<m;i++) 55 for(int j=1;j<=9;j++) 56 ret+=f[i][j][pos]; 57 for(int i=m;i;i--){ 58 for(int j=1;j<p[i];j++) 59 ret+=f[i][j][pos]; 60 if(p[i]==0) break; 61 if(Hash[pos]%p[i]!=0) 62 break; 63 pos=find(Hash[pos]/p[i]); 64 } 65 return ret; 66 } 67 68 inline ll calc(ll x){ 69 ll ret=0; 70 for(int i=1;i<=cnt;i++){ 71 ret+=solve(x/Hash[i],i); 72 } 73 return ret; 74 } 75 76 int main(){ 77 //freopen("umnozak.in","r",stdin); 78 //freopen("umnozak.out","w",stdout); 79 init(); 80 ll L,R; 81 scanf("%lld%lld",&L,&R); 82 printf("%lld\n",calc(R)-calc(L-1)); 83 }
没有什么不可能。