Codeforces Contest 1073 problem E Segment Sum —— 数位DP求一段区间内位数不同的数量小于k的所有数的和

202 篇文章 5 订阅
82 篇文章 1 订阅

You are given two integers l and r (l≤r). Your task is to calculate the sum of numbers from l to r (including l and r) such that each number contains at most k different digits, and print this sum modulo 998244353.

For example, if k=1 then you have to calculate all numbers from l to r such that each number is formed using only one digit. For l=10,r=50 the answer is 11+22+33+44=110.

Input
The only line of the input contains three integers l, r and k (1≤l≤r<1018,1≤k≤10) — the borders of the segment and the maximum number of different digits.

Output
Print one integer — the sum of numbers from l to r such that each number contains at most k different digits, modulo 998244353.

Examples
inputCopy
10 50 2
outputCopy
1230
inputCopy
1 2345 10
outputCopy
2750685
inputCopy
101 154 2
outputCopy
2189
Note
For the first example the answer is just the sum of numbers from l to r which equals to 50⋅512−9⋅102=1230. This example also explained in the problem statement but for k=1.

For the second example the answer is just the sum of numbers from l to r which equals to 2345⋅23462=2750685.

For the third example the answer is 101+110+111+112+113+114+115+116+117+118+119+121+122+131+133+141+144+151=2189

题意:

给你一段区间,和一个数k,让你求出这段区间内所有(这个数的所有位上的数字不同不超过k个)的数的和

题解:

一看就是数位DP,dp[i][j][k]表示在第i位,j是状压,表示当前已经有哪些数被访问过,k表示当前是否为前导0阶段,如果不判断前导0的话,01234就是5个数,可能会超过k导致答案漏算,dp是一个存pair的三维数组,first表示dp[i][j][k]有多少种,second表示dp[i][j][k]累加的答案是多少,这个想想就能明白,假设当前到了1,下一位枚举的时候可以从0枚举到9,那么这个1就要*10*10,也就是10加到19,如果后面还有一位,那就要*100,那么我们就先把所有10的幂次求出来,这就是答案了。

#include<bits/stdc++.h>
using namespace std;
#define pl pair<ll,ll>
#define ll long long
const ll mod=998244353;
ll l,r;
int k,lenl,lenr;
int a[20],b[20];
pl dp[20][(1<<10)][2];
ll all=0,Pow[25];
pl dfsr(int pos,int mx,int s,bool x)
{
    if(pos<=0)
        return (pl){1,0};
    if(!mx&&~dp[pos][s][x].second)
        return dp[pos][s][x];
    pl ans=(pl){0,0};
    int up=mx?a[pos]:9;
    for(int i=0;i<=up;i++)
    {
        pl t;
        if(x==0&&!i)
            t=dfsr(pos-1,mx&&(i==up),0,0);
        else
        {
            int ss=s|((x||i)<<i);
            if(__builtin_popcount(ss)>k)
                continue;
            t=dfsr(pos-1,mx&&(i==up),ss,1);
        }
        ans.first=(ans.first+t.first)%mod;
        ans.second=(ans.second+t.second+1ll*i*Pow[pos-1]%mod*t.first%mod)%mod;
    }
    if(!mx)
        dp[pos][s][x]=ans;
    return ans;
}
pl dfsl(int pos,int mx,int s,bool x)
{
    if(pos<=0)
        return (pl){1,0};
    if(!mx&&~dp[pos][s][x].second)
        return dp[pos][s][x];
    pl ans=(pl){0,0};
    int up=mx?b[pos]:9;
    for(int i=0;i<=up;i++)
    {
        pl t;
        if(x==0&&!i)
            t=dfsl(pos-1,mx&&(i==up),0,0);
        else
        {
            int ss=s|((x||i)<<i);
            if(__builtin_popcount(ss)>k)
                continue;
            t=dfsl(pos-1,mx&&(i==up),ss,1);
        }
        ans.first=(ans.first+t.first)%mod;
        ans.second=(ans.second+t.second+1ll*i*Pow[pos-1]%mod*t.first%mod)%mod;
    }
    if(!mx)
        dp[pos][s][x]=ans;
    return ans;
}
int main()
{
    memset(dp,-1,sizeof(dp));
    scanf("%lld%lld%d",&l,&r,&k);
    Pow[0]=1;
    for(int i=1;i<=20;i++)
        Pow[i]=Pow[i-1]*10%mod;
    while(r)
    {
        a[++lenr]=r%10;
        r/=10;
    }
    l--;
    while(l)
    {
        b[++lenl]=l%10;
        l/=10;
    }
    ll ans=dfsr(lenr,1,0,0).second;
    memset(dp,-1,sizeof(dp));
    ans=(ans-dfsl(lenl,1,0,0).second+mod)%mod;
    printf("%lld\n",ans);
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值