【BZOJ】1799: [Ahoi2009]self 同类分布

【题意】给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数。1 ≤ a ≤ b ≤ 10^18

【算法】数位DP

【题解】

感觉这种方法很暴力啊。

枚举数位和1~162(不能枚举0,不然会模0,相当于除0),记忆化f[pos][sum][val],sum表示当前数位和,val表示数字取模枚举的数位和。

每次sum+i和(val*10+i)%MOD转移。

sum用减法优化,即记忆化(MOD-sum),但是枚举过程中都要memset,导致效率低下,记忆化效果很差。

要什么方法才能跑1.3s啊,%%%。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=20;
ll f[maxn][200][200],a[maxn],b[maxn],c[maxn],n,MOD;
ll dfs(int pos,int sum,int val,int limit){
    if(sum>MOD)return 0;
    if(pos==-1){if(sum==MOD&&val==0)return 1;else return 0;}
    if(!limit&&~f[pos][MOD-sum][val])return f[pos][MOD-sum][val];

    int up=limit?a[pos]:9;
    ll ans=0;
    for(int i=0;i<=up;i++){
        ans+=dfs(pos-1,sum+i,(val*10+i)%MOD,limit&&i==up);
    }
    if(!limit)f[pos][MOD-sum][val]=ans;
    return ans;
}
int main(){
    ll A,B,cntb=0,cntc=0;
    scanf("%lld%lld",&A,&B);
    A--;
    while(A){
        b[cntb++]=A%10;
        A/=10;
    }
    while(B){
        c[cntc++]=B%10;
        B/=10;
    }
    ll ans=0;
    for(int i=1;i<=162;i++){//不能模0啊!!!模0也是除0啊!!!
        memset(f,-1,sizeof(f));
        MOD=i;
        n=cntb;
        for(int j=0;j<n;j++)a[j]=b[j];
        ans-=dfs(n-1,0,0,1);
        n=cntc;
        for(int j=0;j<n;j++)a[j]=c[j];
        ans+=dfs(n-1,0,0,1);
    }
    printf("%lld",ans);
    return 0;
}

时间: 2024-12-13 14:18:47

【BZOJ】1799: [Ahoi2009]self 同类分布的相关文章

bzoj 1799: [Ahoi2009]self 同类分布 题解

[原题] 1799: [Ahoi2009]self 同类分布 Time Limit: 50 Sec  Memory Limit: 64 MB Submit: 554  Solved: 194 [Submit][Status] Description 给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数. Input Output Sample Input 10 19 Sample Output 3 HINT [约束条件]1 ≤ a ≤ b ≤ 10^18 Source Day1 [分析]

BZOJ 1799: [Ahoi2009]self 同类分布 ( 数位dp )

传送门   也不是很难,微坑的一点是dp数组在扫描每一次数位和的时候都要初始化, 因为没有存 用来mod的总数位和x 的位置... ... 然后这个dp的式子也稍微想了一下...还是练题太少... 1 #include<cmath> 2 #include<cstdio> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<queue> 7

bzoj 1799: [Ahoi2009]self 同类分布

数位DP|记忆化搜索 听隔壁巨佬说DP和记搜可以互相转换 显然这个题是可以用记忆化搜索过的,那我们应传哪几个参数? 首先就是记搜最基本的位置标记. 然后就是枚举的数字各位之和,以及取模之后的余数(判断能否整除某个数). 最后就是判边界的参数. 当我们搜到最后一位时如果余数为0,并且各位之和=mod,那就直接返回1,否则返回零. 显然我们把这几个数装进数组里,是开不下的. long long 为8字节 \(8\times 20\times 200\times 200\times 200 \div1

bzoj 1799: [Ahoi2009]self 类似的分布 解读

[原标题] 1799: [Ahoi2009]self 同类分布 Time Limit: 50 Sec  Memory Limit: 64 MB Submit: 554  Solved: 194 [id=1799" style="color:blue; text-decoration:none">Submit][id=1799" style="color:blue; text-decoration:none">Status] Descr

bzoj1799 [Ahoi2009]self 同类分布

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1799 [题解] 一眼看过去,数位dp! 想了想,发现数字和最多也就是$m = 9 \times 18 = 162$种,好像不是很大. 考虑枚举每种数字和$p$,做一遍dp. 设$f_{i,j,k}$表示到第$i$位,当前真实数字模$p$余$j$,当前所有数字的和为$k$的方案数.(不考虑前导0问题) 这个可以通过一个$O(18\times p^2 \times 10)$的动态规划解决. 接

手打AC的第2道数位DP:BZOJ1799: [Ahoi2009]self 同类分布

先讲下个人对于数位DP的看法吧... 挺难理解的 首先需要明白的一点:前缀和很重要 其次:必须用到记忆化搜索(本人蒟蒻,必须要用这种方法降低难度) 然后呢,需要判断约束的条件:(1.前缀0(有时需要,有时不需要):2.数位的取值(基本都需要)) 比如这道题,乍一看无思路,然后呢.... 就会出现很神奇的事情 大胆尝试(第一次)3维(题解本来是4维的...) 居然就XJB A了... 略显蛋疼... 回归正题*2: 本题的状态有些难找,但是由于数位最多的也只有pos,所以就可枚举所有的数位和...

HYSBZ - 1799 self 同类分布

self 同类分布 HYSBZ - 1799 给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数.Sample Input 10 19 Sample Output 3 Hint [约束条件]1 ≤ a ≤ b ≤ 10^18 约束:一个数是它自己数位和的倍数,直接dp根本找不到状态,枚举数位和,因为总就162,然后问题就变成了一个数%mod=0,mod是枚举的,想想状态:dp[pos][sum][val],当前pos位上数位和是sum,val就是在算这个数%mod,(从高位算  *10

[AHOI2009]同类分布

[AHOI2009]同类分布 求区间\([l,r]\)内的数,满足自己能整除自己各个数位上的和的数的个数,\(l,r\leq 10^{18}\). 解 不难得知设\(f_n\)为n以内的满足条件的数,答案即\(f_r-f_l\),因为递推中要表现整除,可以考虑摸递推. 要表现各个数位的和,又要变现长度,而且还要表现摸数,还有余数,故设\(f[i][j][k][l]\)表示i位的数,各数位的和为j,摸数为k,余数为l的数的方案数,因此不难有 \[f[i][j][k][l]=\sum_{p=0}^9

BZOJ 1800: [Ahoi2009]fly 飞行棋

二次联通门 : BZOJ 1800: [Ahoi2009]fly 飞行棋 /* BZOJ 1800: [Ahoi2009]fly 飞行棋 乱搞一下就好 */ #include <cstdio> #include <iostream> #define rg register inline void read (int &n) { rg int c = getchar (); for (n = 0; !isdigit (c); c = getchar ()); for (; i