hihocoder #1033 交错和问题的思考

题目要求如下:
时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
给定一个数 x,设它十进制展从高位到低位上的数位依次是 a0, a1, …, an - 1,定义交错和函数:

f(x) = a0 - a1 + a2 - … + ( - 1)^(n - 1)a(n - 1)

例如:

f(3214567) = 3 - 2 + 1 - 4 + 5 - 6 + 7 = 4

给定 l, r, k,求在 [l, r] 区间中,所有 f(x) = k 的 x 的和,即:

1405402477702.png

输入
输入数据仅一行包含三个整数,l, r, k(0 ≤ l ≤ r ≤ 1018, |k| ≤ 100)。

输出
输出一行一个整数表示结果,考虑到答案可能很大,输出结果模 109 + 7。

提示
对于样例 ,满足条件的数有 110 和 121,所以结果是 231 = 110 + 121。

更多样例:

Input
4344 3214567 3
Output
611668829
Input
404491953 1587197241 1
Output
323937411
Input
60296763086567224 193422344885593844 10
Output
608746132
Input
100 121 -1
Output
120

样例输入
100 121 0
样例输出
231

思路:
按照动态规划问题求解,若求a与b之间满足要求的数,我们只需从0开始至b满足要求的数-从0至a满足要求的数,即[0,b+1)-[0,a)。
题目的意思是求一个数从高位至低位依次交错求和,将数字拆开,如四位数5845,可以拆为5,8,4,5;
交错和的函数定义:
f(x) = a0 - a1 + a2 - … + ( - 1)^(n - 1)a(n - 1);
f(5845)=5-8+4-5=-4==5-f(845)=5-(8-4+5);
现在定义函数
f(i)(j)(k)表示i位以j为最高位且交错和为k的数的所有和;
例如f(2)(9)(0)=495,表示00到以9为最高位、位数为2的所有交错和为0的数之和为495,即
f(2)(9)(0)=99+88+77+66+55+44+33+22+11+0=495;
g(i)(j)(k)为0到i位以j为最高位且交错和为k的数的个数;
求解这两个数位数组的求解公式:
假设且f(6)(8)(0),即求解000000-899999中所有交错和为0的数之和。
在之前提到:
f(5845)=5-8+4-5=-4==5-f(845)=5-(8-4+5);
那么推导:
f(a1a2a3a4)=a1-f(a2a3a4)这是必然的。
则000000-899999求解可以化为:000000-7999999与800000-899999满足交错和k的数之和。800000-899999交错和k可以分解为00000-99999满足交错和为8-k的数和,再加上00000-99999满足交错和为8-k的数个数*800000;
所以f(i)(j)(k)=f(i)(j-1)(k)+f(i-1)(9)(j-k)+10^(i-1)g(i-1)(9)(j-k)*j;
g(i)(j)(k)=g(i)(j-1)(k)+g(i-1)(9)(j-k);

上面求出的是高位为j,次以为是9的数与0之间满足交错和的数之和。在问题给出的数如121求解,则将问题化解为:0-99;100-119;120-121满足交错和为k的数之和。
接下来是代码:

#include "iostream"
#include "math.h"
long long int MOD=1000000007;
using namespace std;
int buffer=120;

long long int f[19][10][400]={0},g[19][10][400]={0};
void init()
{
    int i,j,k;
    i=1;
    for (j=0;j<=9;j++)
    {
        for (k=-120;k<=120;k++)

        {   
            if(j-1>=0)//j>=1,//
            {
                f[i][j][k+buffer]+=f[i][j-1][k+buffer];
                g[i][j][k+buffer]+=g[i][j-1][k+buffer];
            }
            if (j==k)
            {
                f[i][j][k+buffer]+=j;
                g[i][j][k+buffer]+=1;
            }

        }
    }
    for (i=2;i<=19;i++)
    {
        for (j=0;j<=9;j++)
        {
            for (k=-120;k<=120;k++)
            {
                long long int tem=0;
                if (j-1>=0)
                {
                    tem=f[i][j-1][k+buffer];
                //  tem=tem%MOD;
                    g[i][j][k+buffer]=g[i][j-1][k+buffer];
                }
                tem+=f[i-1][9][j-k+buffer];
                tem=tem%MOD;
                //tem+=((long long int)pow(10,i-1)%MOD*g[i-1][9][j-k+buffer]*j)%MOD;
                long long int P=(long long int)pow(10,i-1);
                P=P%MOD;
                P=P*j;
                P=P%MOD;
                long long int Num=g[i-1][9][j-k+buffer];
                Num=Num%MOD;
                P=P*Num;
                P=P%MOD;
                tem=tem+P;
                tem=tem%MOD;
                f[i][j][k+buffer]=tem;
                g[i][j][k+buffer]+=g[i-1][9][j-k+buffer];


            }
        }
    }
}
long long int search(long long int a,int k)
{
    int bit[19]={0};
    long long int b=a;
    int len=0;
    long long int tem=0;
    while (b>0)//将数字以位数形式存储//
    {
        bit[++len]=b%10;
        b=b/10;
    }
    for (int i=1;i<=len;i++)
    {
        if(bit[i]>0)
        {


            if (i==len)//数字首位第一个数;
            {
                tem+=f[i][bit[i]-1][k+buffer]-f[i][0][k+buffer];
                tem=(tem+MOD)%MOD;
                for (int j=i-1;j>0;--j)
                {
                    tem+=f[j][9][k+buffer]-f[j][0][k+buffer];
                    tem=(tem+MOD)%MOD;
                }
            }
            else   //数位非数字的首位;
        {
                int x=1;
                int val=0,value;
                for (int j=len;j>i;j--)
                {
                    val+=x*bit[j];
                    x=-x;
                }
                value=(k-val)*x;
                tem+=f[i][bit[i]-1][value+buffer];
                tem=tem%MOD;
                //tem+=(long long int)((a/(long long int)(pow(10,i)))%MOD*((long long int)pow(10,i)*g[i][bit[i]-1][value+buffer])%MOD)%MOD;
                long long int P=(long long int)pow(10,i);
                long long int temp=(long long int)(a/P)*P;
                temp=temp%MOD;
                long long int Num=g[i][bit[i]-1][value+buffer];
                Num=Num%MOD;
                temp=temp*Num;
                temp=temp%MOD;
                tem=tem+temp;
                tem=tem%MOD;

            }
        }
    }
    return tem;
}
int main()
{
    init();
    int k;
    long long int l,r;
    cout<<f[2][9][0+buffer];
    while(cin>>l>>r>>k)
        cout<<((search(r+1,k)-search(l,k))+MOD)%MOD<<endl;

    return 0;
}

代码测试了最高为18位的数,与网上代码结果对比相同,但提交是RE,暂时不清楚问题出在哪。

网上有许多成功的例子,思路都一样,不过用的是数位DP记忆化搜索,代码如下:

#include <cstdio>
#include <cstring>
long long mod=1000000007;
long long base[20];
long long l,r,k,bit[20],bt,yy;
struct node {
    long long s,n;//s代表数字和,n代表数字个数
};
node dp[20][400];//状态转移
node dfs(long long pos,long long target,long long limit)//数位dp,基本可以算是模板啦
{
    node t;
    t.s=t.n=0;
    if (pos==0) {               //处理到最后一位,直接判断返回
        if (target==100)
        t.n=1;
        return t;
    }
    if ((limit==0)&&(dp[pos][target].n!=-1)) return dp[pos][target];
    long long tail=limit?bit[pos]:9;
    long long sgn=((yy-pos)%2)?(-1):(1);//确定符号
    long long head;
    if (pos==yy)head =1;
    else head=0;//确定搜索的起点和终点
    for (int i=head;i<=tail;i++)
    {
        node tmp=dfs(pos-1,target-i*sgn,(limit==1)&&(i==bit[pos]));
        if ((tmp.n)>0){
            t.n+=tmp.n;
            long long q;
            q=((((tmp.n%mod)*base[pos])%mod)*i)%mod;//结果的同余处理
            t.s+=(tmp.s)%mod;
            t.s%=mod;
            t.s+=q;
            t.s%=mod;//每一步都要同余
        }
    }
    if (limit==0) dp[pos][target]=t;
    return t;
}
long long cal(long long x,long long y)
{
    long long ans=0;
    if (x==-1) return 0;
    if (x==0) return 0;
    bt = 0;
    while (x)
    {
        bt++;
        bit[bt]=x%10;
        x/=10;
    }
    for (yy=1;yy<=bt;yy++){
        memset(dp,-1,sizeof dp);
        ans+=dfs(yy,y+100,yy==bt).s;//对于每个长度为yy的数字进行处理
        ans=(ans+mod)%mod;
    }
    return ans;
}
int main()
{
    base[1]=1;
    for (int i=2;i<=19;i++)
        base[i]=(base[i-1]*10)%mod;
    scanf("%lld%lld%lld",&l,&r,&k);
    {
        printf("%lld",(cal(r,k)-cal(l-1,k)+mod)%mod);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值