jzoj4799 我的快乐时代

31 篇文章 0 订阅
5 篇文章 0 订阅

题目大意

F(x)=X ,求 F(1..n)
n<=1018

想法

首先10^18肯定是 log 解的

我们按照数位DP的思想,去考虑10^(k-1)~10^k-1的F(i)和

0~9:
1*1+2*2+3*3… = 285

10~99:
XX ,考虑这一对的贡献,那就是两倍的
1*1+1*2+1*3…+1*9 + 2*1+2*2+2*3+….9*1
=2*(1*45+2*45+3*45…9*45)
=2*45*45

100~999:
XXX 先考虑那一对的贡献,2*45*45*10 ,就是相当于XX中间再插一个数。
再考虑中间的贡献,左右两边最高位9种,最低位10种,共9*10*285

… 以此类推。

然后呢,对于一个数比如32074
我们先做0~29999
就相当于,万位有0~2=3种可能,其他位10种,按照上面的计算即可
再做30000~31999
万位1种可能 ,千位0~2两种可能,其他位10种,
以此类推即可。

CODE

#include <cstdio>
#include <iostream>
#include <cmath>
#include <cmath>
#define MO 1000000007LL
#define M(x,y) ((y)-(x)+1)
typedef long long ll;

using namespace std;
ll l,r;
ll num[20],can[20],numst[20],numed[20],tmpnum[20];
void resolve(ll x, ll* o) {
    for (ll kk=0; kk<20; kk++) o[kk]=0;
    while (x>0) {
        o[++o[0]]=x%10;
        x/=10;
    }
}
ll F(ll x) {
    ll rt=0;
    resolve(x,num);
    long long ed=0;
    ll st=0,p2,p,s;
    ll maybe=0;
    ll b=10;
    while (b<=x) {
        resolve(b-1,tmpnum);
        for (ll j=1; j<=tmpnum[0]/2; j++) {
            maybe=1;
            for (ll k=1; k<=tmpnum[0]; k++) {
                if (k != j && k != M(j,tmpnum[0])) {
                    if (k==tmpnum[0]) {
                        maybe=maybe*9%MO;
                    } else {
                        maybe=maybe*10%MO;
                    }
                }
            }
            rt=(rt+maybe%MO*2*45*45%MO)%MO;
        }
        if (tmpnum[0]%2) {
            maybe=1;
            for (ll k=1; k<=tmpnum[0]; k++){
                if (k != tmpnum[0]/2+1) {
                    if (k==tmpnum[0]) {
                        maybe=maybe*9%MO;
                    } else {
                        maybe=maybe*10%MO;
                    }
                }
            } 
            rt=(rt+maybe%MO*285%MO)%MO;
        } 
        b*=10;
        if (b==1000000000000000000) break;
    }
    st=b/10;
    for (ll i=num[0]; i>0; i--) {
        ed+=((ll)pow(10,i-1))*num[i];
        if (i==num[0]) ed--;
        if (i==1) ed++;
        if (st>ed) continue;
        if (ed>x) continue;
        resolve(st,numst);
        resolve(ed,numed);
        for (ll i=1; i<=numed[0]; i++)  can[i]=numed[i]-numst[i]+1;

        if (numst[numed[0]]==0) can[numed[0]]--;
        for (ll j=1; j<=numed[0]/2; j++) {
            p=0;
            for (ll k=numst[j]; k<=numed[j]; k++) {
                p+=k;   
            }
            p2=0;
            for (ll k=numst[M(j,numed[0])]; k<=numed[M(j,numed[0])]; k++) {
                p2+=k;  
            }
            maybe=1;
            for (ll k=1; k<=numed[0]; k++) {
                if (k != j && k != M(j,numed[0])) {
                    maybe=maybe*can[k]%MO;
                }
            }
            rt=(rt+2*p*p2%MO*maybe%MO)%MO;
        }
        if (numed[0]%2) {
            maybe=1;
            s=0;
            for (ll k=numst[numed[0]/2+1]; k<=numed[numed[0]/2+1]; k++)  {
                s+=k*k;
            }
            for (ll k=1; k<=numed[0]; k++){
                if (k != numed[0]/2+1) {
                    maybe=maybe*can[k]%MO;
                }
            } 
            rt=(rt+s%MO*maybe%MO)%MO;
        }
        st=ed+1;
    }
    return rt%MO;
}
int main() {
    cin>>l>>r;
    cout<< (F(r)+MO-F(l-1))%MO <<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值