zoukankan      html  css  js  c++  java
  • UPC 2223: A-Number and B-Number(数位DP+二分)

    积累点:

    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那个位置。

    代码:

    #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));
        }
    }
  • 相关阅读:
    c# WinForm开发 DataGridView控件的各种操作总结(单元格操作,属性设置)
    linux服务之rsyslog
    java实现第五届蓝桥杯李白打酒
    java实现第五届蓝桥杯李白打酒
    java实现第五届蓝桥杯猜字母
    java实现第五届蓝桥杯猜字母
    java实现第五届蓝桥杯大衍数列
    java实现第五届蓝桥杯大衍数列
    redis 安装启动及设置密码<windows>
    redis密码设置、访问权限控制等安全设置
  • 原文地址:https://www.cnblogs.com/shinecheng/p/3601235.html
Copyright © 2011-2022 走看看