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; }