洛谷 P2602 [ZJOI2010]数字计数(数位DP)

题目链接:https://www.luogu.com.cn/problem/P2602

这道题大意是求某区间内数字0-9出现的次数。

分别统计区间内存在1个1,2个1,3个1……的数的个数,存在1个2,2个2,……,1个9,2个9,……的数的个数,然后求累加:累加的是次数*个数。

注意前导0和DFS最后的边界的处理。

num:哪一个数码        cnt:次数      now:已出现的次数

dp第一维记录pos,第二维记录now,第三维记录cnt,里面存ans。

AC代码:

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 #include<cstring>
 5 using namespace std;
 6 typedef long long ll;
 7 ll dp[20][20][20],ans[50];
 8 int a[50];
 9 ll DFS(int pos,int now,int limit,int lead,int cnt,int num){
10     if(pos==0) return now==cnt;//注意边界处理 
11     if(!limit&&!lead&&dp[pos][now][cnt]!=-1) return dp[pos][now][cnt];
12     int up=limit?a[pos]:9;
13     ll ans=0;
14     for(int i=0;i<=up;i++){
15         if(lead&&i==0) ans+=DFS(pos-1,now,limit&&i==a[pos],1,cnt,num);
16         else ans+=DFS(pos-1,now+(i==num),limit&&i==a[pos],0,cnt,num);
17     }
18     if(!lead&&!limit) dp[pos][now][cnt]=ans;
19     return ans;
20 }
21 void solve(ll x,int flag){
22     int len=0;
23     memset(dp,-1,sizeof(dp));
24     while(x){
25         a[++len]=x%10;
26         x/=10;
27     }
28     for(int i=0;i<=9;i++){
29         for(int j=1;j<=len;j++){//枚举出现的次数 
30             if(flag) ans[i]+=j*DFS(len,0,1,1,j,i);
31             else ans[i]-=j*DFS(len,0,1,1,j,i);
32         }
33     }
34 }
35 int main(){
36     ll n,m;
37     scanf("%lld%lld",&n,&m);
38     solve(n-1,0); solve(m,1);
39     for(int i=0;i<=9;i++) printf("%lld ",ans[i]);
40     return 0;
41 }
AC代码

猜你喜欢

转载自www.cnblogs.com/New-ljx/p/12450518.html