难受啊,怎么又遇到我不会的题了(捂脸)
如题,这是一道数位DP,随便找了个博客居然就是我们大YZ的……果然nb,然后就是改改模版++注释就好的了,直接看注释吧,就是用1~B - 1~A-1而已,枚举全部位然后判一下是不是上限边缘和前导零就OK
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long LL; LL bit[20]; int len,a[20]; LL as[10],ret[20];//ret表示假如是上限边缘,后面有多少种填的方式 LL f[20][2][2]; //f[k][fg][z]表示当前这个数x,搞到第k位的时候的数目 //fg表示该位是不是在上限边缘 z表示目前是否还是前导零 LL dfs(int x,int k,int fg,int z) { if(k==len+1)return 0; if(f[k][fg][z]!=-1)return f[k][fg][z]; LL ans=0; if(fg==1)//如果前面的部分处于上限边缘 { for(int i=0;i<a[k];i++)//不填上限边缘,枚举填什么 { if(z==1&&i==0)ans+=dfs(x,k+1,0,1); else { ans+=dfs(x,k+1,0,0); if(i==x)ans+=bit[len-k]; } //如果i是要统计次数的那个数字,而且不是在前导零的时候,而且还不是在上限的边缘 //那么后面几位的数字有多少种填法x就出现了几次 } //该位填上限边缘 if(z==1&&a[k]==0)ans+=dfs(x,k+1,1,1); else { ans+=dfs(x,k+1,1,0); if(a[k]==x)ans+=ret[k]; } //如果填的是要统计次数的那个数字 而且不是在前导零的时候 //那么后面的数字最多有多少种填法x就出现了几次 } else//没有限制 { for(int i=0;i<=9;i++)//同上 { if(z==1&&i==0)ans+=dfs(x,k+1,0,1); else { ans+=dfs(x,k+1,0,0); if(i==x)ans+=bit[len-k]; } } } f[k][fg][z]=ans; return ans; } char s[20]; void cl()//把A减1 { int t=len; while(t>1&&s[t]=='0'){s[t]='9';t--;} s[t]--; if(s[t]==0) { len--; for(int i=1;i<=len;i++)s[i]=s[i+1]-'0'; } } int main() { freopen("dream.in","r",stdin); freopen("dream.out","w",stdout); bit[0]=1;for(int i=1;i<=18;i++)bit[i]=bit[i-1]*10; memset(as,0,sizeof(as)); scanf("%s",s+1);len=strlen(s+1);cl(); for(int i=1;i<=len;i++)a[i]=s[i]-'0'; //0的情况,所以填的方案都要+1 ret[len]=1;for(int i=len-1;i>=1;i--)ret[i]=a[i+1]*bit[len-i-1]+ret[i+1]; for(int i=0;i<=9;i++) { memset(f,-1,sizeof(f)); as[i]-=dfs(i,1,1,1); } scanf("%s",s+1);len=strlen(s+1); for(int i=1;i<=len;i++)a[i]=s[i]-'0'; ret[len]=1;for(int i=len-1;i>=1;i--)ret[i]=a[i+1]*bit[len-i-1]+ret[i+1]; for(int i=0;i<=9;i++) { memset(f,-1,sizeof(f)); as[i]+=dfs(i,1,1,1); } for(int i=0;i<9;i++)printf("%d ",as[i]); printf("%d ",as[9]); return 0; }