ZOJ 3962

就是统计1~n中出现的各个数字的次数,当然是在16进制下。

不过有个区间问题的小技巧,统计从 [x,y] 可以转换成 从 [1,y] 减去 [1,x-1]。

不过要分类讨论一下,因为有可能会出现溢出,从ffffffff +1 得到 00000000 就是溢出了。

因为 n < 1e9 所以只会溢出一次。

 // 290ms

#include<cstdio>
#include<cmath>
#include<cctype>

const int cost[] = {6,2,5,5,4,5,6,3,
                    7,6,6,5,4,5,5,4};

long long solve(long long n, int k) {
    long long i = 1;   // 位数
    long long cnt = 0;
    long long low = 0;
    long long tmp = n;
    while(n) {
        long long t = n % 16;
        n /= 16;
        low = tmp % i;
        if(t < k) {
            cnt += n*i;
        } else if(t > k) {
            cnt += (1+n)*i;
        } else {
            cnt += n*i;
            cnt += low+1;
        }
        i *= 16;
    }
    return cnt;
}
int Hex(char c){
    if(isdigit(c))
        return c-'0';
    return c - 'A' + 10;
}
int main() {
    int t;
    scanf("%d",&t);
    for(int i=0;i<t;i++){
        long long n;
        long long x=0;
        char str[15];
        scanf("%lld%s",&n,str);
        for(int i=0;i<8;i++)
            x = (x<<4) + Hex(str[i]);
        long long cnt = 0;
        long long ans = 0;

        if((n-1)+x <= 4294967295ll){ // 没有溢出
            long long tmp;
            for(int i=1;i<16;i++){
                tmp = solve((n-1ll)+x,i);
                cnt += tmp;
                ans += tmp*cost[i];
            }
            for(int i=1;i<16;i++){
                tmp = solve(x-1,i);
                cnt -= tmp;
                ans -= tmp*cost[i];
            }
            ans += (n*8-cnt)*cost[0];
        }else {
            long long tmp;
            for(int i=1;i<16;i++){
                tmp = solve(4294967295ll,i);
                cnt += tmp;
                ans += tmp*cost[i];
            }
            for(int i=1;i<16;i++){
                tmp = solve(x-1,i);
                cnt -= tmp;
                ans -= tmp*cost[i];
            }
            for(int i=1;i<16;i++){
                tmp = solve((n-2ll)+x-4294967295ll,i);
                cnt += tmp;
                ans += tmp*cost[i];
            }
            ans += (n*8-cnt)*cost[0];
        }
        printf("%lld
",ans);
    }
    return 0;
}
View Code

搜了一下题解发现可以用数位dp做。。

当时倒是也想到了数位dp但是因为做过的都是不定长的,所以就没有想到可以定长为8。

// 250ms

#include<cstdio>
#include<cmath>
#include<cstring>
#include<cctype>

int digit[16];
long long dp[10][100];
const int cost[] = {6,2,5,5,4,5,6,3,
                    7,6,6,5,4,5,5,4};

long long dfs(int d,int sum,bool shangxian){
    if(d == 0)return sum;
    if(!shangxian && dp[d][sum] != -1)
        return dp[d][sum];
    int maxn = shangxian?digit[d]:15;
    long long tmp = 0;
    for(int i=0;i<=maxn;i++){
        tmp += dfs(d-1,sum+cost[i],shangxian && i==maxn);
    }
    return shangxian?tmp:dp[d][sum]=tmp;
}
long long solve(long long x){
    int k = 0;
    if(x<0)return 0;
    memset(digit,0,sizeof(digit));
    while(x){
        digit[++k] = x % 16;
        x /= 16;
    }
    return dfs(8,0,1);
}
int Hex(char c){
    if(isdigit(c))
        return c-'0';
    return c - 'A' + 10;
}
int main() {
    int t;
    memset(dp,-1,sizeof(dp));
    scanf("%d",&t);
    while(t--){
        long long n;
        long long x=0;
        char str[15];
        scanf("%lld%s",&n,str);
        for(int i=0;i<8;i++)
            x = (x<<4) + Hex(str[i]);
        // 4294967295ll
        if(x+n-1 <= 4294967295ll)
            printf("%lld
",solve(x+n-1)-solve(x-1));
        else
            printf("%lld
",
                   solve(4294967295ll)-solve(x-1)+solve(x+n-2-4294967295ll));
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/kongbb/p/10702004.html