[BZOJ 1799] self 同类分布

Link:

BZOJ 1799 传送门

Solution:

一句话的题目,看得爽,做得烦

一般这类和数位相关的都是数位$dp$吧

不过一开始还是感觉不太可做,毕竟每个数模数不同

 

但要发现,模数最高也只可能为$9*19=171$,

于是只要将数按照他们的数位和(即模数)分类计算即可

这样便暴力解决了模数不同的问题

 

设$dp[sp][sum][rmd][lmt]$表示:

枚举到第$sp$高位,剩下的数的和位$sum$,此时对$mod$余$rmd$时的方案数(lmt表示是否达到上界)

感觉数位$dp$还是用记忆化搜索写起来逻辑比较清晰吧

 

Code:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAXN=175; //n可能为19,sum最大为171 
int vis[20][MAXN][MAXN][2],mod,dgt,cur,num[20];
ll a,b,dp[20][MAXN][MAXN][2];

ll dfs(int sp,int sum,int rmd,int lmt)
{
    if(!sp) return !sum && !rmd;
    if(vis[sp][sum][rmd][lmt]==cur) return dp[sp][sum][rmd][lmt];
    vis[sp][sum][rmd][lmt]=cur;ll ret=0;
    
    int l=max(0,sum-(sp-1)*9),r=min((lmt)?num[sp]:9,sum);
    for(int i=l;i<=r;i++)
        ret+=dfs(sp-1,sum-i,(rmd*10+i)%mod,lmt&(i==num[sp]));
    return dp[sp][sum][rmd][lmt]=ret;
}

ll solve(ll x)
{
    ll ret=0;
    for(dgt=0;x;x/=10) num[++dgt]=x%10;
    for(mod=1;mod<=dgt*9;mod++)
        cur++,ret+=dfs(dgt,mod,0,1);
    return ret;
}

int main()
{
    scanf("%lld%lld",&a,&b);
    printf("%lld\n",solve(b)-solve(a-1));
    return 0;
}

 

Review:

1、少用$memset$,尽量在使用时顺便初始化

为了区分不同次的调用,可以在每一次调用打上不同的标记

 

2、在$dp$时难以处理模数不同的情况

考虑将数据分类后直接暴力所有可能的模数

 

3、$1e18$可能有19位,$MAXN$要设为175

(一开始扫了一眼题解上$MAXN$为165,以后还是要自己算啊……)

 

posted @ 2018-06-30 14:42  NewErA  阅读(242)  评论(0编辑  收藏  举报