题意:给你A和B,让你求0~~B中F(x)< = F(A) 的x的个数,满足F(x) = An * 2n-1 + An-1 * 2n-2 + ... + A2 * 2 + A1 * 1;
分析:当我做到这题的时候一个很清晰的细节就是,给我们10的9次方范围,0.5s的时限,最大的F(x)总和也不到7000,T(测试样例)还很大,容易想到的就是数位DP,至于怎么数位DP,可以发现,它的第i位和第i+1位有关联,于是得到状态转移方程:dp [ i ] [ j ] [ k + 2 ^ ( i - 1 ) * j ] + = sum { dp[ i - 1 ] [ t ] [ k ] } ; dp [ i ] [ j ] [ k ]表示第i位是J的时候总和为K的个数;
代码如下:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
const int maxn = 7000;
int dp[12][12][maxn];
int sum[12][12][maxn];
void init(){
memset(dp,0,sizeof(dp));
memset(sum,0,sizeof(sum));
dp[0][0][0]=1;
for(int i=1;i<11;i++){
for(int j=0;j<=9;j++){
for(int k=0;k<maxn-500;k++){
int t1=k+(1<<(i-1))*j;
for(int t=0;t<=9;t++){
dp[i][j][t1]+=dp[i-1][t][k];
}
if(k==0) sum[i][j][k]=dp[i][j][k];
else
sum[i][j][k]=sum[i][j][k-1]+dp[i][j][k];
}
}
}
}
int w[12];
int dige(int n){
int ret=0;
while(n){
w[++ret]=n%10;
n/=10;
}
return ret;
}
int work(int n,int M){
int dig=dige(n);
// printf("%d\n",dig);
int flag=0;
int ans=0;
for(int i=dig;i>=1;i--){
for(int j=0;j<w[i];j++){
ans+=sum[i][j][M-flag];
}
flag+=w[i]*(1<<(i-1));
if(flag>M) break;
}
return ans;
}
int main(){
int T;
init();
scanf("%d",&T);
int con=1;
while(T--){
int a,b;
scanf("%d %d",&a,&b);
int fa=0;
int wei=0;
while(a){
fa+=(a%10)*(1<<wei);
a/=10;
wei++;
}
printf("Case #%d: %d\n",con++,work(b+1,fa));
}
}