sgu258 Almost Lucky Numbers (数位dp)

题意:

定义一个具有2n位的正整数,其前n位之和与后n位之和相等,则为lucky数。给定一个区间l,r,问有多少个正数可以通过修改某一位数从而变成lucky数?不能含前导0。

tip:

定义dp[i][j][p][q]表示前I位和为j其中数位上最大值是p 最小值是q的方案数。。
我们先把整个区间分开,10,99],[1000,9999],[100000,999999]。。。
然后如果[1,K]完全包含了某个区间,就直接统计把答案统计进去就行了。我们假设k将某个区间给分开了,也就是说k在上面的四个区间中的一个中,那么我们就需要单独考虑一下这个区间怎么做。
如果K的位数是奇数直接统计包含了几个区间就行了,因为奇数位是不可能分割以上区间的。
那么如果是偶数位,求这个小区间中有多少个近似幸运。

在分开这个小区间,例如这个数为2313分为:[1***],[20**],[21**],[22**],[230*],[2310],[2311][2312]一共有多少个符合题意的数字,于是将区间一分为二,如果长度是2n,dp0表示前n个dp1表示后n个。因为不能有前导零,所以dp0的第一位不能是0 初始化的是第一位开始,dp1可以有签到零,所以初始化从0开始。两个dp过程一样:枚举ijpq如果遇到现在是*,那么要填的数从1~9枚举,p,q跟着变,check的时候,枚举dp0[n]…dp1[n]…的sum p,q..首先如果sum相同肯定不行(改一个之后一定不相同),其次,检验
改变后最小值:sum-q(一边的最大值)+0
改变后最大值:sum-p(一边的最小值)+9
是否在另一个sum中间,注意如果是dp0,第一位也可以改,但p,q没记录它,单独用s[0]和p q比较一下。最后答案相加

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
LL l,r,n;
int num[20],a[20];
LL dp0[10][10*9][10][10],F[20];///前i位和为j 最小为p 最大是q  的方案数
LL dp1[10][10*9][10][10];


void _dp0(string s){
    for(int i = 0 ; i < n ; i++){
        for(int j = 0 ; j <= 9*n ; j++){
            for(int p = 0 ; p <= 9 ; p++){
                for(int q = 0 ; q <= 9 ; q++){
                    dp0[i][j][p][q] = 0;
                }
            }
        }
    }
    if(s[1]=='*'){
        for(int i = 0;i <= 9;i++)   dp0[1][s[0]-'0'+i][i][i] = 1;
    }
    else
        dp0[1][s[0]-'0'+s[1]-'0'][s[1]-'0'][s[1]-'0'] = 1;
    int mk = 1;
    for(int i = mk ; i < n ; i++){
        for(int j = 0 ; j <= 9*n ; j++){
            for(int p = 0 ; p <= 9 ; p++){
                for(int q = 0 ; q <=  9 ; q++){
                    if(dp0[i][j][p][q] > 0){
                        if(s[i+1] == '*'){
                            for(int k = 0 ; k <= 9 ; k ++){
                                dp0[i+1][j+k][min(p,k)][max(q,k)] += dp0[i][j][p][q];
                            }
                        }
                        else{
                            int tmp = s[i+1]-'0';
                            dp0[i+1][j+tmp][min(p,tmp)][max(q,tmp)] += dp0[i][j][p][q];
                        }
                    }
                }
            }
        }
    }
}
void _dp1(string s){
    for(int i = 0 ; i < n ; i++){
        for(int j = 0 ; j <= 9*n ; j++){
            for(int p = 0 ; p <= 9 ; p++){
                for(int q = 0 ; q <= 9 ; q++){
                    dp1[i][j][p][q] = 0;
                }
            }
        }
    }
    if(s[0]=='*'){
        for(int i = 0;i <= 9; i++)   dp1[0][i][i][i] = 1;
        //cout <<"a  a"<<endl;
    }
    else{
        dp1[0][s[0]-'0'][s[0]-'0'][s[0]-'0'] = 1;
        //cout <<"aaaaaaaa"<<endl;
    }
    int mk = 0;
    //cout <<"n = "<<n<<endl;
    for(int i = mk ; i < n-1 ; i++){
        for(int j = 0 ; j <= 9*n ; j++){
            for(int p = 0 ; p <= 9 ; p++){///最小
                for(int q = 0 ; q <=  9 ; q++){///最大
                    if(dp1[i][j][p][q] == 0)    continue;
                    //cout <<"  ss   "<<endl;
                    if(s[i+1] == '*'){
                        for(int k = 0 ; k <= 9 ; k ++){
                            dp1[i+1][j+k][min(p,k)][max(q,k)] += dp1[i][j][p][q];
                        }
                    }
                    else{
                        int tmp = s[i+1]-'0';
                        dp1[i+1][j+tmp][min(p,tmp)][max(q,tmp)] += dp1[i][j][p][q];
                    }
                }
            }
        }
    }
}
LL check(string s){
    //cout <<s<<endl;
    if(s.size() == 2){
        if(s[1] == '*') return 9;
        else{
            if(s[0] == s[1])    return 0;
            return 1;
        }
    }
    string s0 = "",s1 = "";
    n = s.size()/2;
    for(int i = 0 ; i < n ; i++)    s0.push_back(s[i]);
    for(int i = 0 ; i < n ; i++)    s1.push_back(s[i+n]);
    _dp0(s0);
    _dp1(s1);

    LL ans = 0;
    for(int sum0 = 0 ; sum0 <= 9*n ; sum0++){
        for(int p0 = 0 ; p0 <= 9 ; p0++){
            for(int q0 = 0 ; q0 <= 9 ; q0++){
                if(dp0[n-1][sum0][p0][q0] == 0) continue;
                for(int sum1 = 0 ; sum1 <= 9*n ;sum1++){
                    if(sum1 == sum0)    continue;
                    //cout <<"sum1 = "<<sum1<<endl;
                    for(int p1 = 0 ; p1 <= 9 ; p1 ++){
                        for(int q1 = 0 ;q1 <= 9 ; q1++){
                            if(dp1[n-1][sum1][p1][q1] == 0 ) continue;///min: -max+0     max: -min+9
                            //cout <<"bb = "<<endl;
                            if( ((min(sum0-q0+0,sum0+1-(s[0]-'0')) <= sum1) && (max(sum0+9-p0,sum0+9-(s[0]-'0')) >= sum1) )||
                                ((    sum1-q1+0                    <= sum0) && (    sum1+9-p1                    >= sum0) )){
                                //cout <<"hah"<<endl;
                                ans += dp0[n-1][sum0][p0][q0]*dp1[n-1][sum1][p1][q1];
                            }
                        }
                    }
                }
            }
        }
    }
    //cout <<"ans    - >   "<<ans<<endl;
    return ans;
}
LL getnum(int cnt){
    string s0 = "",s1 = "";
    LL ans= 0;
    for(int t = cnt ; t >= 1; t--){
        int d = num[t];
        for(int i =  (t == cnt?1:0) ; i < d ;  i++){
            s1 = s0;
            s1.push_back('0'+i);
            for(int j = t-1;j >= 1;j--) s1.push_back('*');
            ans += check(s1);
        }
        s0.push_back('0'+d);
    }
    return ans;
}
LL sov(int k){
    if(k < 10)  return 0;
    int cnt = 0,tmp = k;
    LL ans = 0;
    memset(num,0,sizeof(num));
    while(tmp > 0){
        num[++cnt] = tmp%10;
        tmp /= 10;
    }
    for(int i = 2 ; i < cnt ;i+=2)
        ans += F[i];
    if((cnt&1) || k == 0){
        return ans;
    }
    ans += getnum(cnt);
    return ans;
}
int main(){
    F[2]=81;F[4]=7389;F[6]=676133;F[8]=62563644;
    while(cin >> l >>r)
    cout <<sov(r+1)-sov(l)<<endl;
}
//1200 1210
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值