数位dp:Educational Codeforces Round 53 (Rated for Div. 2) E. Segment Sum

给出上下界,让你求出其中满足条件:不同的数字的数量不超过k个的数字的总和,答案模998244353,比如123里不同的数字个数为3,113里不同的数字个数为2,111里不同的数字个数为1。

跟普通的数位dp相比,这道题的不同在于是求总和,不是求数字的个数,但我们可以在求数字个数的基础上再进行求和,以下可以看代码注释。   

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<stack>
#include<map>
#include<vector>
#include<queue>
#include<set>
#include<iomanip>
#include<cctype> 
#include<ctime>
using namespace std;
typedef long long ll;
#define edl putchar('\n')
#define sscc ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define ROF(i,a,b) for(int i=a;i>=b;i--)
#define FORLL(i,a,b) for(ll i=a;i<=b;i++)
#define ROFLL(i,a,b) for(ll i=a;i>=b;i--)
#define mst(a) memset(a,0,sizeof(a))
#define mstn(a,n) memset(a,n,sizeof(a))
#define zero(x)(((x)>0?(x):-(x))<eps)
#define si(a) scanf("%d",&a)
#define sl(a) scanf("%lld",&a)
#define sd(a) scanf("%lf",&a)
#define ss(a) scanf("%s",a)
inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
inline ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
int month[15]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int dir[8][2]={{-1,0},{1,0},{0,-1},{0,1},{-1,-1},{1,1},{1,-1},{-1,1}};
const int MAXN=2e5+5;
const int INF=1<<30;
const long long mod=998244353;
const double eps=1e-8;
const ll inff=0x3f3f3f3f3f3f3f3f;
ll dp[25][1030][12];/*记录答案总和的数组*/
ll d[25][1030];/*记录答案个数的数组*/
ll t[25];/*10的幂次方,节省时间*/
int a[25];/*记录每一位数字*/
int f[25];/*2的幂次方,节省时间*/
int k[1030];/*状态压缩,记录某个状态有多少个不同的数字*/
int p;/*题目问的最大不同数量*/
struct num
{
	ll a,b;/*a对应d数组,b对应dp数组*/
}s;
num dfs(int pos,int now,int sta,int limit)
{
	if(pos==0)
	{
		return (num){1,now};
	}	
	if(!limit&&dp[pos][sta][now]!=-1)
	{
		return (num){d[pos][sta],dp[pos][sta][now]};
	}
	int up=limit?a[pos]:9,g;
	ll ans=0,cnt=0;
	FOR(i,0,up)
	{
		if(sta==0&&i==0)/*前导0的特殊判断*/ 
		{
			s=dfs(pos-1,i,sta,limit&&i==up);
			cnt+=s.a;/*答案个数*/
			ans+=s.b;/*答案总和*/
			cnt%=mod;
			ans%=mod;
		}
		else
		{
			g=sta|f[i];
			if(k[g]<=p)
			{
				s=dfs(pos-1,i,g,limit&&i==up);
				cnt+=s.a;/*答案个数*/
				ans+=s.b;/*答案总和*/
				cnt%=mod;
				ans%=mod;
			}
		}	
	}
	ans=(ans+cnt*t[pos]%mod*(ll)now%mod)%mod;
	if(!limit)
		d[pos][sta]=cnt,dp[pos][sta][now]=ans;
	return (num){cnt,ans};
}
ll solve(ll x)
{
	if(x==0) return 0;
    int pos=0;
    while(x)
    {
        a[++pos]=x%10;
        x/=10;
    }
    a[pos+1]=0;
    return dfs(pos,0,0,1).b;
}
int main()
{
    ll n,m;
    f[0]=1,t[0]=1;
    FOR(i,1,18)
    f[i]=f[i-1]*2,t[i]=t[i-1]*10,t[i]%=mod;
    FOR(i,0,1024)
    {
    	int j=i,cnt=0;
    	while(j)
    	{
    		cnt+=j%2;
    		j/=2;
		}
		k[i]=cnt;
	}
    FOR(i,0,20)
    FOR(j,0,1024)
    {
    	d[i][j]=-1;
	    FOR(I,0,9)
	    dp[i][j][I]=-1;
	}
    //scanf("%d",&T);
    //while(1)
    {
    	scanf("%lld%lld%d",&n,&m,&p);
        n=solve(n-1);
        m=solve(m);
		printf("%lld\n",(m-n+mod)%mod);
    }
    return 0;
}

  

posted @ 2018-11-01 16:10  诚信肥宅  阅读(186)  评论(0编辑  收藏  举报