CF 1073 E. Segment Sum

https://codeforces.com/problemset/problem/1073/E

 

题意:[l,r]中,出现0—9数字的种类数不超过k的数的和

 

dp[i][j][0/1] 表示 dfs到第i位以后,数字出现的情况状态为j能转移的状态,是否有上界限制的数字的和

f[i][j] 表示 dfs到第i位以后,数字出现的情况状态为j能转移的状态,没有上界限制的数字的个数

枚举这一位填什么

加上这一位的贡献,加上填上这一位之后后面数的贡献

 

数字出现状态:若i出现过,则状态j的第i位二进制位为1

 

#include<cstdio>
#include<cstring>

using namespace std;

typedef long long LL;

const int mod=998244353;

int dp[20][(1<<10)+2][2],f[20][(1<<10)+2];
int ans,m;
int a[20],pow10[20];
bool va[(1<<10)+2];

int k;

LL dfs(int dep,int s,int lim,bool zero)
{
    if(!va[s]) return 0;
    if(!lim && f[dep][s]!=-1) return f[dep][s];
    if(!dep) return 1;
    int up=lim ? a[dep] : 9,nl,news,tmp;
    int sum=0;
    for(int i=0;i<=up;++i)
    {
        if(zero && !i) news=0; 
        else news=s|1<<i;
        nl=lim && a[dep]==i;
        tmp=dfs(dep-1,news,nl,zero && !i);
        dp[dep][s][lim]+=1ll*tmp*i%mod*pow10[dep-1]%mod;
        dp[dep][s][lim]%=mod;
        dp[dep][s][lim]+=dp[dep-1][news][nl];
        dp[dep][s][lim]%=mod;
        sum+=tmp;
        sum%=mod;
    }
    if(!lim) f[dep][s]=sum;
    return sum;
}

void solve(long long n,int ty)
{
    int len=0;
    while(n) a[++len]=n%10,n/=10;
    memset(f,-1,sizeof(f));
    memset(dp,0,sizeof(dp));
    dfs(len,0,1,1);
    ans+=ty*dp[len][0][1]; 
    ans%=mod;
    if(ans<0) ans+=mod; 
}

bool check(int s)
{
    int p=0;
    for(int i=0;i<=9;++i)
        if(s&1<<i) p++;
    if(p<=k) return 1;
    else return 0;
}

int main()
{
    LL l,r;
    scanf("%lld%lld%d",&l,&r,&k);
    pow10[0]=1;
    for(int i=1;i<19;++i) pow10[i]=1ll*pow10[i-1]*10%mod; 
    m=1<<10;
    for(int i=0;i<m;++i) va[i]=check(i);
    solve(r,1);
    solve(l-1,-1);
    printf("%d",ans); 
}

 

posted @ 2020-02-27 21:19  TRTTG  阅读(...)  评论(...编辑  收藏