SDUT 2609 A-Number and B-Number 二分+数位dp

题意是:给出Anum 的定义.为 包含7或者是该数可以被7整除的数。  a[i]=Anum;

Bnum的定义是,为Anum的子集但是 如果i为Anum,a[i]对应的值就不是Bnum。

即如 A[7] 对应的Anum=35 不是Bnum 所以 B[7] 为37(第7个Bnum数)


思路: 先二分一个数mid ,找到<=mid 的mid最小的区间满足等于n个Bnum的数 ,此数就是

,如何求区间内Bnum 的数量呢,,先求Anum的数量,然后对Anum的数量(即为下标)这个区间再求Anum 的数量,

两者相减就是Bnum的数量了 、。

即:


此题需要注意的地方,1.会超 long long 所以要用unsigned longlong  否则会TLE, 2 定义的类型容易全部都写成int类型。 3 二分的写法。


#include <cstdio>         ///此代码书写的时候出现了LL 定义成了int的情况
#include<cstring>
#include<iostream>
#include<algorithm>
#include<map>
#include<cmath>
#include<cstdlib>
#define LL unsigned long long
#define bug puts("***********")
#define INF 0x3f3f3f3f

using namespace std;

const LL R=((LL)1<<63)-1;
int num=0;
int bit[100];
LL dp[100][100][2];
LL DFS(int p,int m,int have,int flag)
{
    if(p==0) return have||(m==0);
    if(flag&&dp[p][m][have]!=-1) return dp[p][m][have];
    int k=flag?9:bit[p];
    LL sum=0;
    for(int i=0; i<=k; i++)
    {
        sum=sum+DFS(p-1,(m*10+i)%7,i==7||have,flag||i!=k);
    }
    if(flag) dp[p][m][have]=sum;
    return sum;
}
LL solve(LL x)
{

    if(x<0)
        return 0;
    num=0;
    while(x)
    {
        bit[++num]=x%10;
        x/=10;
    }
    return DFS(num,0,0,0)-1;
}
LL get(LL m)
{
    LL ans1=solve(m);              /// LL
    return ans1-solve(ans1);  ///Anum - num(下标是Anum的数的 个数)
}
int main()
{
    LL n;
    while(~scanf("%lld",&n))
    {
        memset(dp,-1,sizeof(dp));
        /// cout<<get(n)<<endl;
        LL high =R;
        //cout<<pow(2,63)-1;
        LL low=0;
        LL ans=0;
        LL num1,num2,num3;
        while(low<=high)
        {
            LL mid=(high+low)>>1;

            num2=get(mid);


            /// 这种二分的方法不对,因为并不是唯一的一个mid对应一个num2,
            ///而是存在多个mid对应一个num2 的情况,并且应该取符合条件的最小mid才行
//            if(num2==n)    .
//            {
//                ans=mid;
//                break;
//            }
//            else if(num2>n)
//                high=mid-1;
//            else
//                low=mid+1;

            if(num2<n)
                low=mid+1;
            else
            {
                ans=mid;
                high=mid-1;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}
    


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值