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;
    }
        






  • 相关阅读:
    windows 按时自动化任务
    Linux libusb 安装及简单使用
    Linux 交换eth0和eth1
    I.MX6 GPS JNI HAL register init hacking
    I.MX6 Android mmm convenient to use
    I.MX6 GPS Android HAL Framework 调试
    Android GPS GPSBasics project hacking
    Python windows serial
    【JAVA】别特注意,POI中getLastRowNum() 和getLastCellNum()的区别
    freemarker跳出循环
  • 原文地址:https://www.cnblogs.com/zswbky/p/6717891.html
Copyright © 2011-2022 走看看