zoukankan      html  css  js  c++  java
  • Educational Codeforces Round 53 (Rated for Div. 2) E. Segment Sum (数位dp求和)

    题目链接:https://codeforces.com/contest/1073/problem/E

    题目大意:给定一个区间[l,r],需要求出区间[l,r]内符合数位上的不同数字个数不超过k个的数的和(并且模998244353)

    例如求区间[10,50],k=1,答案为ans=(11+22+33+44)%998244353=110.

    Examples

    input
    Copy
    10 50 2
    output
    Copy
    1230
    input
    Copy
    1 2345 10
    output
    Copy
    2750685
    input
    Copy
    101 154 2
    output
    Copy
    2189


    解题思路:首先我们用数位dp求出区间[l,r]上合法数的个数并不难,可以采用二进制记录每一个数是否出现,难点在于如何对合法的数进行求和求和,我们肯定不能一个数一个数进行求和,,我们可以考虑在每个位放每个数,计算该位的该数对答案的贡献度。总权值和等于比它低位的权值和+当前位的权值。
    需注意前导0的情况。
    代码:
    #include<bits/stdc++.h>
    using namespace std;
    typedef unsigned long long ll;
    const ll mod=998244353;
    int gcd(int a,int b){return b?gcd(b,a%b):a;}
    int lcm(int a,int b){return a/gcd(a,b)*b;}
    int a[20],k;
    ll l,r;
    struct node{
        ll a,b;  //a表示合法数的个数,b表示权值的和 
    }dp[20][2525];
    int work(int x){  //计算数位中有多少个不同的数字 
        int cnt=0;
        for(int i=0;i<=10;i++)
        if(x>>i&1)cnt++;
        return cnt;
    } 
    node dfs(int pos,int sta,int limit,bool invalid){
    //sta记录包含的不同数,invalid记录前导0 
        if(pos==0) return node{1,0}; 
        if(!limit&&dp[pos][sta].a!=0)
        return dp[pos][sta];
        int up=limit?a[pos]:9;
        node ans,tmp;
        ans.a=0; ans.b=0;  //局部变量得初始化 
        for(int i=0;i<=up;i++){
            int x=sta|(int)(pow(2,i)); //更新状态 
            if(work(x)>k) continue; //不同数字个数超过k,不合法剪枝 
            if(invalid&&i==0){  //前导都为0,状态不用更新 
                tmp=dfs(pos-1,sta,limit&&i==up,invalid&&i==0);
            }else{
                tmp=dfs(pos-1,x,limit&&i==up,invalid&&i==0);
            }
            tmp.a%=mod; tmp.b%=mod;
            ans.a=(ans.a+tmp.a)%mod; //累计合法数的个数 
            ll y=pow(10,pos-1);
            ans.b=(ans.b+tmp.b+1ll*y%mod*i%mod*tmp.a%mod)%mod; //累计合法数的权值 
        }
        if(!limit)
        dp[pos][sta]=ans;
        return ans;
    }
    ll solve(ll x){
        int pos=0;
        while(x){
            a[++pos]=x%10;
            x/=10;
        }
        return dfs(pos,0,1,true).b;
    }
    int main(){
        cin>>l>>r>>k;
        cout<<(solve(r)-solve(l-1)+mod)%mod<<endl; //防止答案为负 
        return 0;
    } 
  • 相关阅读:
    Luogu5860 「SWTR-03」Counting Trees
    $NOIP1998$ 题解报告
    $NOIP1997$ 题解报告
    $NOIP2008$ 题解报告
    $NOIP2004$ 题解报告
    $NOIP2010$ 题解报告
    $NOIP2009$ 题解报告
    Luogu P1879 玉米田 题解报告
    CH301 任务安排2 题解报告
    $[NOIp2017]$ 逛公园 $dp$/记搜
  • 原文地址:https://www.cnblogs.com/zjl192628928/p/10631175.html
Copyright © 2011-2022 走看看