1. 程式人生 > >【Educational Codeforces Round 53 (Rated for Div. 2) E. Segment Sum】 數位DP

【Educational Codeforces Round 53 (Rated for Div. 2) E. Segment Sum】 數位DP

E. Segment Sum

題意

l

r
k
題意很簡單,求l到r之間的所有數中不同數位數不超過k的數之和

1 < = l < = r < = 1 0 18    1 < = k < = 10 1<=l<=r<=10^{18} \ \ 1<=k<=10

做法

d p 一眼就知道是數位dp
而且我們也很容易想到狀態的定義
d p ( 1 < < 10 ) 數位dp時要往下傳遞都有哪些數位出現過,這個狀態是(1<<10)的
由於我們要統計和,每個狀態要儲存兩個值
滿 滿足狀態的數字個數和當前狀態數字對答案的貢獻

坑點

0 d p 0 由於本題中前導0對狀態有影響,注意數位dp時前導0的問題

程式碼

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
const ll Mod = 998244353ll;

typedef pair<ll,ll> pll;
#define Se second
#define Fi first
ll l,r,k;
ll pow_[20];
int cal(ll x)
{
    int ans=0;
    while(x)
    {
        if(x&1) ans++;
        x/=2;
    }
    return ans;
}
pll dp[20][1<<12][2];
ll a[20];
pll dfs(ll len,ll state,bool limit,bool lead)
{
    if(len==0) return pll(1,0);//遍歷結束,無需算對答案的貢獻,但是要像正常數位dp一樣統計滿足條件的個數
    if(!limit&&dp[len][state][lead].Se) return dp[len][state][lead];//注意帶前導0的數位dp套路
    pll ans=pll(0,0);
    int up=limit?a[len]:9;
    for(int i=0;i<=up;i++)
    {
        ll temp=state|((lead||i)<<i);
        //若當前有前導0而且當前數位為0,則狀態不更新
        if(cal(temp)>k) continue;
        //不滿足答案的狀態要去掉
        pll tmp=dfs(len-1,temp,limit&&i==a[len],lead||i);
        ans.Fi=(ans.Fi+tmp.Fi)%Mod;
        ans.Se=(ans.Se+tmp.Se+(1LL*i*pow_[len-1]%Mod*tmp.Fi)%Mod)%Mod;
        //當前數位對答案的貢獻為當前數位所代表的值為i*pow[len-1]
        //當前數位為i的數字個數為tmp.Fi
        //總貢獻為:i*pow_[len-1]%Mod*tmp.Fi
    }
    return dp[len][state][lead]=ans;
}
ll solve(ll x)
{
    for(int i=0;i<20;i++)
    {
        for(int j=0;j<(1<<12);j++)
        {
            for(int l=0;l<2;l++)
            {
                dp[i][j][l].Fi=0;
                dp[i][j][l].Se=0;
            }
        }
    }
    memset(a,0,sizeof(a));
    int len=0;
    while(x)
    {
        a[++len]=x%10;
        x/=10;
    }
    return dfs(len,0,true,0).Se%Mod;
}
int main()
{
    pow_[0]=1;
    for(int i=1;i<20;i++) pow_[i]=pow_[i-1]*10%Mod;//每一位的貢獻預處理
    scanf("%lld%lld%lld",&l,&r,&k);
    printf("%lld\n",(solve(r)-solve(l-1)+Mod)%Mod);
    return 0;
}