P2602 [ZJOI2010]数字计数(递推+数位dp写法)

P2602 [ZJOI2010]数字计数

递推写法:

主要观察到对于所有小于1000的数每一个数字1,2,3,...都是平均出现的(不考虑前导零)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+100;
#define debug(x) cout<<#x<<':'<<x<<endl;
int bits[maxn];
ll dp_up[maxn],dp_down[maxn],zahn[maxn],a,b;
void solve(long long x,long long *dp)
{
    int len=-1;ll tempnum=x;
    for(ll i=x;i;i/=10) bits[++len]=i%10;
    for(int i=len;i>=0;i--){
        for(int j=0;j<=9;j++)   dp[j]+=i*(zahn[i]/10)*bits[i];//后面的位数是平均的
        for(int j=0;j<bits[i];j++)  dp[j]+=zahn[i];//当前首位的贡献
        dp[bits[i]]+=(tempnum-=zahn[i]*bits[i])+1;
        dp[0]-=zahn[i];//按位处理前导零
    } 
}
int main()
{
    scanf("%lld %lld",&a,&b);
    zahn[0]=1;
    for(int i=1;i<=15;i++)zahn[i]=10*zahn[i-1];
    solve(a-1,dp_down);
    solve(b,dp_up);
    for(int i=0;i<=9;i++)
    printf("%lld ",dp_up[i]-dp_down[i]);
    system("pause");
}
数位dp写法:

1.要考虑前导零

2.要记得dp每次赋值为-1

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define debug(x) cout<<#x<<':'<<x<<endl;
const int maxn=25;
ll dp[maxn][2][2][10][maxn],bitsa[maxn],bitsb[maxn],a,b;
ll dfs(int pos,int lim,int lead,int dig,ll sum ,ll *bits){//lead为0表示有前导零
    if(pos==0) return sum;//处理边界
    ll &ans=dp[pos][lim][lead][dig][sum];//数位dp本质记忆化搜索
    if(ans!=-1) return ans;
    else ans=0;
    int up=lim?bits[pos]:9;
    for(int i=0;i<=up;i++){
        ans+=dfs(pos-1,(i==up)&&lim,lead||i,dig,sum+((i||lead)&&(i==dig)),bits);
    }
    return ans;
}
int main(){
    scanf("%lld%lld",&a,&b);
    int lena=0;a--;
    for(ll i=a;i;i/=10) bitsa[++lena]=i%10;
    int lenb =0;
    for(ll i=b;i;i/=10)bitsb[++lenb]=i%10;
    for(int i=0;i<=9;i++){memset(dp,-1,sizeof(dp));
        ll ans=dfs(lenb,1,0,i,0,bitsb);memset(dp,-1,sizeof(dp));
        ans-=dfs(lena,1,0,i,0,bitsa);
        printf("%lld ",ans);
    }
    //system("pause");
}
原文地址:https://www.cnblogs.com/zx0710/p/14378913.html