题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1799

   https://www.luogu.org/problemnew/show/P4127

经典dp!

一个数能被它的各位和整除,在L-R内有多少个。

1.数位dp的套路,先预处理出第i位、后面任意的所有情况。

  因为涉及整除,所以状态有“模当前数余几”;

  因为涉及各位和,所以状态有“i位和为j”和“模k”;

好了我们有了一份会MLE的四维代码,而且有会超时的18位预处理,答案好歹是正确的。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. using namespace std;
  5. typedef long long ll;
  6. const int sx=*;
  7. ll tmp,f[][][][],cnt1,cnt2;
  8. int lmm,a[];
  9. ll pw(int k)
  10. {
  11. ll mul=;
  12. for(int i=;i<=k;i++)
  13. mul*=;
  14. return mul;
  15. }
  16. void pre()
  17. {
  18. for(int k=;k<=sx;k++)
  19. f[][][k][]=;
  20. int lm=;
  21. for(int i=;i<=;i++)
  22. {
  23. lm=i*;
  24. for(int j=;j<=lm;j++)
  25. for(int k=;k<=sx;k++)
  26. for(int l=;l<k;l++)
  27. for(int p=;p<=&&j-p>=;p++)
  28. f[i][j][k][l]+=f[i-][j-p][k][((l-p*pw(i-))%k+k)%k];
  29. // if(i==1&&j==p&&k==p)printf("j=%d k=%d l=%d p=%d ff=%lld\n",j,k,l,p,f[i][j][k][l]);
  30.  
  31. }
  32. }
  33. void chl()
  34. {
  35. lmm=;
  36. while(tmp)
  37. {
  38. a[++lmm]=tmp%;
  39. tmp/=;
  40. }
  41. }
  42. ll calc()
  43. {
  44. int t=;
  45. ll cnt=,lj=;
  46. for(int i=lmm;i;i--)
  47. {
  48. int ll=a[i];if(i==)ll++;
  49. for(int p=;p<ll;p++)
  50. for(int s=max(,t+p);s<=sx;s++)
  51. cnt+=f[i-][s-t-p][s][(s-(lj+p*pw(i-))%s)%s];
  52. t+=a[i];lj+=a[i]*pw(i-);
  53. }
  54. return cnt;
  55. }
  56. int main()
  57. {
  58. scanf("%lld",&tmp);tmp--;
  59. pre();
  60. chl();
  61. cnt1=calc();
  62. // printf("(%lld)\n",cnt1);
  63. scanf("%lld",&tmp);
  64. chl();
  65. cnt2=calc();
  66. // printf("(%lld)\n",cnt2);
  67. printf("%lld",cnt2-cnt1);
  68. return ;
  69. }

MLE+TLE

在bzoj 50s 下应该还不会超时,考虑解决MLE。

2.发现pre转移的时候,模数s是固定的,即每个状态由模数相同的状态转移过来。

  所以只要把模数s的枚举放在最外面,就可以省掉这一维,代价是每一个s都要预处理一遍。

  尽可能优化一点。

    1)发现L和R的预处理有一部分是重复的,所以把R - L这一部分也在循环里实现而不是最后再减;

    2)(微弱)其实预处理到lm[1]-1位就行了。

bzoj上能过了(30s+),洛谷上超时一个点,而且其他点好慢……

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. using namespace std;
  5. typedef long long ll;
  6. ll f[][][],l,r,mul[],ans;
  7. int lm[],a[][],sx;
  8. void pw()
  9. {
  10. mul[]=;
  11. for(int i=;i<=lm[];i++)
  12. mul[i]=mul[i-]*;
  13. }
  14. void pre(int s)
  15. {
  16. memset(f,,sizeof f);
  17. f[][][]=;//
  18. int lmm=;
  19. for(int i=;i<=lm[];i++)
  20. {
  21. lmm=min(s,i*);
  22. for(int j=;j<=lmm;j++)
  23. for(int l=;l<s;l++)//l<j是错的(为何)
  24. for(int p=;p<=&&j-p>=;p++)
  25. f[i][j][l]+=f[i-][j-p][((l-p*mul[i-])%s+s)%s];//考虑l-p*pw(i-1)<0
  26. }
  27. }
  28. void chl(int k,ll tmp)
  29. {
  30. while(tmp)
  31. {
  32. a[k][++lm[k]]=tmp%;
  33. tmp/=;
  34. }
  35. }
  36. ll calc(int k,int s)
  37. {
  38. ll cnt=;
  39. // printf("s=%d\n",s);
  40. int t=;ll lj=;
  41. for(int i=lm[k];i;i--)
  42. {
  43. int llm=a[k][i];if(i==)llm++;
  44. for(int p=;p<llm&&s-t-p>=;p++)
  45. cnt+=f[i-][s-t-p][(s-(lj+p*mul[i-])%s)%s];//考虑lj+p*pw(i-1)==0
  46. t+=a[k][i];lj+=a[k][i]*mul[i-];
  47. // printf("i=%d cnt=%d\n",i,cnt);
  48. }
  49. return cnt;
  50. }
  51. int main()
  52. {
  53. scanf("%lld%lld",&l,&r);
  54. chl(,l-);chl(,r);
  55. sx=(lm[]-)*+a[][lm[]];pw();
  56. for(int s=;s<=sx;s++)
  57. {
  58. pre(s);
  59. ans+=calc(,s)-calc(,s);
  60. }
  61. printf("%lld",ans);
  62. return ;
  63. }

TLE

3.发现大家写的都是记忆化深搜。

  为什么比较快呢?因为它不需要把一些用不到的状态也预处理出来。

  但具体是什么我也不清楚。详见代码?

  然后发现大牛的代码:写得好好!于是近乎抄了下来。

  这里只要有最后那个d=0时的限制就不用记录各位和为j了,只要最后整个各位和等于s就行。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. using namespace std;
  5. typedef long long ll;
  6. ll input()
  7. {
  8. int f=;ll s=;char ch=getchar();
  9. while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
  10. while(ch>=''&&ch<=''){s=s*+ch-'';ch=getchar();}
  11. return s*f;
  12. }
  13. const int N=,M=;
  14. ll x,y,dp[][M][N][N];
  15. int cnt,a[M],dfn[][M][N][N],tot,mod;
  16. ll calc(bool p,int d,int s,int m)//p为有无限制 ,d是第几位,s是模数也是各位和 ,m是前面填好的东西%s余数
  17. {
  18. if(!d)return (!s&&!m);//初值,第0位时仅当s为0(个位填好后整个的各位和为s)、m为0(整除)时为1
  19. if(dfn[p][d][s][m]==tot)return dp[p][d][s][m];
  20. dfn[p][d][s][m]=tot;ll tmp=;
  21. int l=max(,s-*(d-)),r=min(((p)?:a[d]),s);//r<=s
  22. for(int i=l;i<=r;i++)tmp+=calc(p|(i<a[d]),d-,s-i,(m*+i)%mod);//i是当前位填几
  23. return dp[p][d][s][m]=tmp;
  24. }
  25. ll solve(ll ret)
  26. {
  27. for(cnt=;ret;ret/=)a[++cnt]=ret%;
  28. ll ans=;int sx=cnt*;
  29. for(mod=;mod<=sx;mod++)tot++,ans+=calc(,cnt,mod,);
  30. return ans;
  31. }
  32. int main()
  33. {
  34. x=input();y=input();
  35. return printf("%lld",solve(y)-solve(x-)),;
  36. }

有点奇怪的是下面这份代码数组范围应该都够,但仅改数组范围就能修复一个RE(变成上边那个TLE),也就是说数组越界?

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. using namespace std;
  5. typedef long long ll;
  6. ll f[][][],l,r,mul[],ans;
  7. int lm[],a[][],sx;
  8. void pw()
  9. {
  10. mul[]=;
  11. for(int i=;i<=lm[];i++)
  12. mul[i]=mul[i-]*;
  13. }
  14. void pre(int s)
  15. {
  16. memset(f,,sizeof f);
  17. f[][][]=;//
  18. int lmm=;
  19. for(int i=;i<=lm[];i++)
  20. {
  21. lmm=min(s,i*);
  22. for(int j=;j<=lmm;j++)
  23. for(int l=;l<s;l++)//l<j是错的(为何)
  24. for(int p=;p<=&&j-p>=;p++)
  25. f[i][j][l]+=f[i-][j-p][((l-p*mul[i-])%s+s)%s];//考虑l-p*pw(i-1)<0
  26. }
  27. }
  28. void chl(int k,ll tmp)
  29. {
  30. while(tmp)
  31. {
  32. a[k][++lm[k]]=tmp%;
  33. tmp/=;
  34. }
  35. }
  36. ll calc(int k,int s)
  37. {
  38. ll cnt=;
  39. // printf("s=%d\n",s);
  40. int t=;ll lj=;
  41. for(int i=lm[k];i;i--)
  42. {
  43. int llm=a[k][i];if(i==)llm++;
  44. for(int p=;p<llm&&s-t-p>=;p++)
  45. cnt+=f[i-][s-t-p][(s-(lj+p*mul[i-])%s)%s];//考虑lj+p*pw(i-1)==0
  46. t+=a[k][i];lj+=a[k][i]*mul[i-];
  47. // printf("i=%d cnt=%d\n",i,cnt);
  48. }
  49. return cnt;
  50. }
  51. int main()
  52. {
  53. scanf("%lld%lld",&l,&r);
  54. chl(,l-);chl(,r);
  55. sx=(lm[]-)*+a[][lm[]];pw();
  56. for(int s=;s<=sx;s++)
  57. {
  58. pre(s);
  59. ans+=calc(,s)-calc(,s);
  60. }
  61. printf("%lld",ans);
  62. return ;
  63. }

一个RE

真是经典。以后也许要回顾回顾了呢。

人家的代码好好……

bzoj1799(洛谷4127)同类分布(月之谜)的更多相关文章

  1. 洛谷P4127同类分布

    传送 我们要在dfs的板子里记录哪些量呢?当前填的所有数的和sum?当前填的数构成的数值all? sum可以留下,数值就扔掉叭.数值最大是1e18,要是留下,在g数组里有一维的大小是1e18.也许可以 ...

  2. 洛谷 P4127 [AHOI2009]同类分布 解题报告

    P4127 [AHOI2009]同类分布 题目描述 给出两个数\(a,b\),求出\([a,b]\)中各位数字之和能整除原数的数的个数. 说明 对于所有的数据,\(1 ≤ a ≤ b ≤ 10^{18 ...

  3. 【BZOJ1799】[AHOI2009]同类分布(动态规划)

    [BZOJ1799][AHOI2009]同类分布(动态规划) 题面 BZOJ 洛谷 题解 很容易想到数位\(dp\),然而数字和整除原数似乎不好记录.没关系,直接枚举数字和就好了,这样子就可以把整除原 ...

  4. [BZOJ1799][AHOI2009]同类分布(数位DP)

    1799: [Ahoi2009]self 同类分布 Time Limit: 50 Sec  Memory Limit: 64 MBSubmit: 1635  Solved: 728[Submit][S ...

  5. BZOJ1799 self 同类分布 数位dp

    BZOJ1799self 同类分布 去博客园看该题解 题意 给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数. [约束条件]1 ≤ a ≤ b ≤ 10^18 题解 1.所有的位数之和&l ...

  6. 洛谷P2024 食物链

    挺神奇 题目描述 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A 吃 B,B 吃 C,C 吃 A. 现有 N 个动物,以 1 - N 编号.每个动物都是 A,B,C 中的一种 ...

  7. 洛谷OJ P1196 银河英雄传说(带权并查集)

    题目描述 公元五八○一年,地球居民迁移至金牛座α第二行星,在那里发表银河联邦 创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展. 宇宙历七九九年,银河系的两大军事集团在巴米利恩星域爆发战争.泰山 ...

  8. [洛谷P1196][NOI2002]银河英雄传说 - 带偏移量的并查集(1)

    Description 公元五八〇一年,地球居民迁至金牛座α第二行星,在那里发表银河联邦创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展. 宇宙历七九九年,银河系的两大军事集团在巴米利恩星域爆发 ...

  9. 洛谷 P2678 跳石头

    题目背景 一年一度的"跳石头"比赛又要开始了! 题目描述 这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石.组委会已经选择好了两块岩石作为比赛起点和终点.在起点和终点之间 ...

随机推荐

  1. 30. Substring with Concatenation of All Words *HARD*

    You are given a string, s, and a list of words, words, that are all of the same length. Find all sta ...

  2. 【转】json与jsonp区别浅析(json才是目的,jsonp只是手段)

    一言以蔽之,json返回的是一串数据:而jsonp返回的是脚本代码(包含一个函数调用): JSON其实就是JavaScript中的一个对象,跟var obj={}在质上完全一样,只是在量上可以无限扩展 ...

  3. ajax传递数组、form表单提交对象数组

    在JSP页面开发中,我们常常会用到form表单做数据提交,由于以前一直只是使用form表单提交单个对象,只要表单文本域的name值和接收的对象的属性名一致,那么传值就没有什么问题.不过,在前几天的开发 ...

  4. javascript的逼格

    1.解释性脚本语言,无需编译,逐行解释运行 2.跨平台性,不依赖操作系统,只需要浏览器支持 javascript引擎:单线程

  5. 简话Angular 08 Angular ajax

    一句话: 它们Angular框架声明周期的各个阶段,常规约定各专注于特定功能,经过处理也可以互相替换 1.功能细分简解 $http 类似JQuery ajax,支持promise $http.json ...

  6. svn: E200009: 'lib/systemd/system/dropbear@.service': a peg revision is not allowed here problem

    case: svn add lib/systemd/system/dropbear@.service svn: E200009: 'lib/systemd/system/dropbear@.servi ...

  7. C#阿里云移动推送列表

    C#阿里云移动推送列表 这个就在上期随笔的基础之上,加一个 函数就行了.   简单的. 附上源码:一下代码只要把参数改一下就可以了,中间几个参数 可以灵活修改 /// <summary> ...

  8. W1002 Symbol 'Create' is specific to a platform

    http://stackoverflow.com/questions/9099892/how-to-use-tformatsettings-create-without-being-specific- ...

  9. property 的详细使用方法

    property(fget=None, fset=None, fdel=None, doc=None)  俗话说条条大路通罗马,同样是完成一件事,Python 其实提供了好几个方式供你选择. prop ...

  10. webpack-dev-server将文件产出到指定目录

    默认情况下webpack-dev-server是将文件产出到内存中,写了一个插件 将文件产出到指定目录, 比较简易啊 哈哈哈 使用代码如下 const WebpackDevServerOutput = ...