今天做的比赛,和队友都有轻微被虐的赶脚。
诶,我做的题就是这个题目了。
题目描述就是对于一个十进制数数位上的每一位当做一个二进制位来求出这个数,这个定义为G(x)。
题目给定你A和B,求在0-B范围内有多少个数x满足G(x)<=G(A)。
这个题目显然是个数位DP哦。可惜我在比赛的时候琢磨了好久才弄出来,诶,深坑队友啊。
这个题目给定的时间比较紧,只有500ms,但是数据也不强,只有10^9。
其实最最重要的优化(也是dp的一部分)就是预处理数组f[i][j](表示i位数构成的值的G函数值为j的种类数)
这样很显然很容易可以得到f[i]和f[i+1]的递推关系呢。
同时由于给定的范围只有10^9,所以对于i,我们只要处理到8就可以咯。你懂的。
同时我们可以先算一下,最大的那个数99999999(8个9)所对应的G函数的值(我算好像不过万吧)这样复杂度完全可以闭着眼睛承受。
于是在求的时候也是用的最最经典的数位DP的求法,这里就不详细说了,新手多刷几个水题就弄明白了。
其实这个题目最最关键的就是我刚刚说的这个预处理,但是这个预处理还是不够的,还要预处理f[i][j]的前缀和。
我们可以用sum[i][j]表示f[i][0]~f[i][j]的和,因为每次我们dp的时候要加的是那个和,所以有了这个预处理我们可以直接调用咯,还不怕数据大,每次都是一样的。
下面上我的代码吧,比赛的时候写的,代码有点乱,希望神犇不要喷我……
#include <iostream> #include <cstring> #define ll long long #define maxn 10000 using namespace std; ll a[64],b[64],c[64],A,B,t,na,nb,f[15][maxn],ans,sum[15][maxn],cas=0; int main() { for (ll i=0; i<33; i++) c[i]=(1<<i); memset(f,0,sizeof f); memset(sum,0,sizeof sum); for (ll i=0; i<10; i++) f[1][i]=1; for (ll i=2; i<=8; i++) { for (ll k=0; k<maxn; k++) { if (f[i-1][k]==0) continue; for (ll j=0; j<10; j++) f[i][k+j*c[i-1]]+=f[i-1][k]; } } /*for (ll i=1; i<=5; i++) { for (ll j=0; j<=5; j++) cout<<f[i][j]<<' '; cout<<endl; }*/ for (ll i=1; i<=8; i++) { sum[i][0]=1; for (ll j=1; j<maxn; j++) sum[i][j]=sum[i][j-1]+f[i][j]; } /*cout<<endl; for (ll i=1; i<=5; i++) { for (ll j=0; j<=5; j++) cout<<sum[i][j]<<' '; cout<<endl; }*/ cin>>t; while (t--) { cin>>A>>B; ans=na=nb=0; memset(a,0,sizeof a); memset(b,0,sizeof b); while (A) a[++na]=A%10,A/=10; while (B) b[++nb]=B%10,B/=10; for (ll i=1; i<na; i++) a[i+1]+=a[i]/2,a[i]%=2; while (a[na]>1) a[na+1]=a[na]/2,a[na]%=2,na++; /*for (ll i=na; i>0; i--) cout<<a[i]; cout<<endl; for (ll i=nb; i>0; i--) cout<<b[i]; cout<<endl;*/ A=0; for (ll i=1; i<=na; i++) A+=a[i]*c[i-1]; for (ll i=nb; i>1; i--) { for (ll j=0; j<b[i]; j++) if (A-j*c[i-1]>=0) ans+=sum[i-1][A-j*c[i-1]]; else break; A-=b[i]*c[i-1]; if (A<0) break; } for (ll i=0; i<=b[1]; i++) if (A>=i) ans++; cout<<"Case #"<<++cas<<": "<<ans<<endl; } return 0; }