第四届山东省赛 A-Number and B-Number [数位dp+二分答案]【动态规划】

题目链接:hrbust的oj上有题目

————————————————————————————————
A-Number and B-Number
Time Limit: 1000 MS Memory Limit: 32768 K
Total Submit: 22(8 users) Total Accepted: 5(5 users) Rating: Special Judge: No
Description
Tom is very interested in number problem. Nowadays he is thinking of a problem about A-number and B-number.

A-number is a positive integer whose decimal form contains 7 or it can be divided by 7. We can write down the first 10 A-number ( a[i] is the ith A-number)

{a[1]=7,a[2]=14,a[3]=17,a[4]=21,a[5]=27,a[6]=28,a[7]=35,a[8]=37,a[9]=42,a[10]=47};

B-number is Sub-sequence of A-number which contains all A-number but a[k] ( that k is a A-number.) Like 35, is the 7th A-number and 7 is also an A-number so the 35 ( a[7] ) is not a B-number. We also can write down the first 10 B-number.

{b[1]=7,b[2]=14,b[3]=17,b[4]=21,b[5]=27,b[6]=28,b[7]=37,b[8]=42,b[9]=47,b[10]=49};

Now Given an integer N, please output the Nth B-number.

Input
The input consists of multiple test cases.

For each test case, there will be a positive integer N as the description. (0<N<2631)

Output
For each test case, output an integer indicating the Nth B-number.

You can assume the result will be no more then 2^63-1.

Sample Input
1
7
100
Sample Output
7
37
470

——————————————————————————————————

题目大意:
A数组就是民间游戏”敲七”的序列
B数组就是 {xxi{A}i{A}}
然后输出B数组中第n个元素即: Bn

解题思路:

如果直接求第i个元素的话 无论是A数组还是B数组都不能求(反正我不会)

但是他求得是第 i 个元素 我们可以换一个角度来思考

首先我们可以用一个入门级别的数位dp来统计小于n的元素个数,

那么反过来 [1,n]中 最小的有x个元素的n 就是第x个元素

那么我们就可以用二分答案来统计了

对于A数组我们很好数位dp
但是对于B数组我们不能直接求出来

但是考虑A与B数组的关系,发现,只要对cal(x)统计的结果ans在统计一下相减即可;

即:

A = cal(x);
B = A - cal(A);

注意
二分溢出的问题
最大值要(1<<63)-1,

附本题代码
——————————————————————————————————————————————

#include<bits/stdc++.h>

using namespace std;
typedef long long int LL;
/********************************/

int num[20];
LL dp[20][8][2];

LL dfs(int pos,int mod,int limit,int status){
    if(pos<0) return (status||mod%7==0);
    if(dp[pos][mod][status]!=-1&&!limit) return dp[pos][mod][status]; //忘写 limit  懵逼1000天有没有

    int endi=9;
    if(limit) endi=num[pos];

    LL res = 0;
    for(int i=0;i<=endi;i++)
        res+=dfs(pos-1,(mod*10+i)%7,limit&&i==endi,i==7||status);

    if(!limit) dp[pos][mod][status] = res;
    return res;
}

LL cal(LL x){
    if(!x) return 0;
    int len=0;
    while(x)   num[len++]=x%10,x/=10;
    return dfs(len-1,0,1,0)-1;
}

void solve(LL x){
    LL l=7,r=(1ll<<63)-1,mid,ans=-1;  //r写成1e18 wa懵逼有没有,,,,

    while(l<=r){
        mid=((r-l)>>1)+l;
        LL tmp = cal(mid);
        tmp -= cal(tmp);
        if(tmp>=x)ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%lld\n",ans);
}

int main(){
//    printf("%lld\n",~(1ll<<63));
    memset(dp,-1,sizeof(dp));
    LL n;
    while(~scanf("%lld",&n)) solve(n);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值