zoukankan      html  css  js  c++  java
  • 第四届 山东省ACM A-Number and B-Number(数位DP+二分 待整理)

    A-Number and B-Number

    Time Limit: 1000MS Memory Limit: 65536KB

    Problem 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.

    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.

    Example Input

    17100

    Example Output

    737470

    Hint 

    Author

     2013年山东省第四届ACM大学生程序设计竞赛


    积累点:


    1: (l&r)+((l^r)>>1) == (l+r)/2 
    2: 注意判断现在是否有限制。当枚举下一个量时,是(isQuery && j==end),不要搞错。
     


    传送门:http://acm.upc.edu.cn/problem.php?id=2223

    题意:

    能被7整除或者含7的数称为A-Number,所有A-Number从小到大写好,下标编号(从1开始),去掉那些下标为A-Number的数,剩下的数称为B-Number。求第N个B-Number是多少。


    思路:


    求A-Number就是简单的数位DP。
    dp[i][mod] 表示所有i位数中,%7==mod 的数的个数
    dp[i][mod] =  (j != 7)  dp[i-1][(mod-(j*10i-1)%7+7)%7]   
                        (j == 7)  10i-1(nowx%10i-1+1)    
                         (j=0~9(end))
    之后 二分答案就行了。[0~B]包含  cal(B) - cal(cal(B))  个B-Number。(B包含的ANumber的数目,就是下标最大。这么大的下标范围内有多少ANumber,减掉,剩下就是BNumber的数量)
    二分的时候注意二分到最小的那个。就是说。二分的可能是这样
    7 7 7 8 8 8
    如果查的是8, 则这时候应该二分到第一个8那个位置

    参考网址:http://www.cnblogs.com/shinecheng/p/3601235.html

    代码:
    #include <cstdio>
    #include <cstring>
    
    long long dp[20][7];
    int num[30];
    long long nowx;
    
    long long dfs(int i, int mod, bool isQuery) {
        if (i == 0) {
            return mod == 0;
        }
        long long &nowdp = dp[i][mod];
        if (!isQuery && ~nowdp) {
            return nowdp;
        }
        int end = isQuery?num[i]:9;
        long long ans = 0;
        long long ten = 1;
        for (int k = 0; k < i-1; k++) ten *= 10;
    
        for (int j = 0; j <= end; j++) {
            if (j == 7) {
                ans += (isQuery&&j==end)?((nowx%ten)+1):ten;  // 这一句要小心。
            } else {
                ans += dfs(i-1, (mod-(j*ten)%7+7)%7, isQuery && j == end);
            }
        }
        if (!isQuery) nowdp = ans;
        return ans;
    }
    
    long long cal(long long x) {
        nowx = x;
        int len = 0;
        if (x == 0) return 0;
        while (x) {
            num[++len] = x%10;
            x/=10;
        }
        return dfs(len, 0, true)-1; // 减掉0
    }
    
    long long solve(long long number) {
        long long l = 0;
        long long r = 10e19; 
        while (l<r) {
            long long mid = (l&r)+((l^r)>>1);
            long long Anum = cal(mid);
            long long Bnum = Anum - cal(Anum);
            if (Bnum >= number) r = mid;
            else l = mid+1;
        }
        return l;
    }
    int main(){ 
        long long n;
        memset(dp, -1, sizeof(dp));
        while (scanf("%lld", &n) != EOF) {
            printf("%lld
    ", solve(n));
        }
    }


    题目大意: 
    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
    • 2
    • 1
    • 2

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

    附本题代码 

    参考网址:http://blog.csdn.net/qq_33184171/article/details/61441552

    #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
    ",ans);
    }
    
    int main(){
    //    printf("%lld
    ",~(1ll<<63));
        memset(dp,-1,sizeof(dp));
        LL n;
        while(~scanf("%lld",&n)) solve(n);
        return 0;
    }


    题意是:给出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 二分的写法。


    参考网址:http://blog.csdn.net/became_a_wolf/article/details/51503211

    #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
    ",ans);
        }
        return 0;
    }
        






  • 相关阅读:
    网络编程
    mysql
    python 基础
    vim 操作
    linux 基本命令
    基本库使用(urllib,requests)
    震撼功能:逐浪CMS全面支持PWA移动生成意指未来
    硬件能力与智能AI-Zoomla!逐浪CMS2 x3.9.2正式发布
    从送外卖到建站售主机还有共享自行车说起-2017年8月江西IDC排行榜与发展报告
    HTTP协议知多少-关于http1.x、http2、SPDY的相关知识
  • 原文地址:https://www.cnblogs.com/zswbky/p/6717891.html
Copyright © 2011-2022 走看看