【数位DP】bnuoj 52813 J. Deciphering Oracles

http://acm.bnu.edu.cn/v3/contest_show.php?cid=9208#problem/J

【AC】

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 typedef long long ll;
  4 ll N,K;
  5 ll dp[20][200];
  6 ll cnt[200];
  7 int sumdigit(ll x)
  8 {
  9     int tot=0;
 10     while(x)
 11     {
 12         tot+=x%10;
 13         x/=10;
 14     }
 15     return tot;
 16 }
 17 int digit[20];
 18 int split(ll x)
 19 {
 20     int ret=0;
 21     while(x)
 22     {
 23         digit[++ret]=x%10;
 24         x/=10;
 25     }
 26     reverse(digit+1,digit+1+ret);
 27     return ret;
 28 }
 29 
 30 void Dp(int len)
 31 {
 32     memset(dp,0,sizeof(dp));
 33     //从高位到低位递推 
 34     for(int i=1;i<digit[1];i++)//最高位 
 35     {
 36         dp[1][i]=1;
 37     }
 38     int sum=digit[1];
 39     for(int i=2;i<=len;i++)
 40     {
 41         for(int j=0;j<200;j++)
 42         {
 43             if(dp[i-1][j])//不为0才有贡献 
 44                 for(int tran=0;tran<10;tran++)//从高到底第i位 
 45                 {
 46                     if(j+tran<200)
 47                     {
 48                         dp[i][j+tran]+=dp[i-1][j];
 49                     }
 50                 }
 51         }
 52         for(int j=1;j<=9;j++) dp[i][j]++;//因为初始化dp[1][i]时dp[1][0]为0,所以要补上000..j这种情况 
 53         for(int j=0;j<digit[i];j++) dp[i][sum+j]++;//因为初始化dp[1][digit[1]]为0,所以要补上只有第i位不同的情况 
 54         sum+=digit[i];
 55     }
 56     dp[len][sum]++;//算的是小于等于x的数,要加上本身 
 57 }
 58  
 59 ll cal(ll X,int sum,int type)
 60 {
 61     int len=split(X);
 62     Dp(len);
 63     if(type==1)//[1,X]中位数和小于sum的总数 
 64     {
 65         ll ans=0;
 66         for(int i=1;i<sum;i++)
 67         {
 68             ans+=dp[len][i];
 69         }
 70         return ans;
 71     }
 72     else        //[1,x]中位数和恰好为sum的总数 
 73     {
 74         return dp[len][sum];
 75     }
 76 }
 77 
 78 ll solve(ll X,ll k)
 79 {
 80     int len=split(X);
 81     Dp(len);
 82     for(int i=0;i<200;i++)
 83     {
 84         cnt[i]=dp[len][i];
 85     }
 86     for(int i=1;i<200;i++)
 87     {
 88         cnt[i]+=cnt[i-1];
 89     }
 90     ll pos=lower_bound(cnt+1,cnt+200,k)-cnt;
 91     k-=cnt[pos-1];    
 92     ll l=1,r=1e18;
 93     while(l<=r)
 94     {
 95         ll mid=(l+r)>>1;
 96         if(cal(mid,pos,0)<k)
 97         {
 98             l=mid+1;
 99         }
100         else
101         {
102             r=mid-1;
103         }
104     }
105     return l;
106 }
107 int main()
108 {
109     while(~scanf("%I64d%I64d",&N,&K))
110     {
111         cout<<cal(N,sumdigit(K),1)+cal(K,sumdigit(K),0)<<" ";
112         cout<<solve(N,K)<<endl;    
113     }
114     return 0;    
115 } 
数位DP

 

转载于:https://www.cnblogs.com/itcsl/p/7407347.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值