bzoj1799(洛谷4127)同类分布(月之谜)
题目: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位预处理,答案好歹是正确的。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int sx=*;
ll tmp,f[][][][],cnt1,cnt2;
int lmm,a[];
ll pw(int k)
{
ll mul=;
for(int i=;i<=k;i++)
mul*=;
return mul;
}
void pre()
{
for(int k=;k<=sx;k++)
f[][][k][]=;
int lm=;
for(int i=;i<=;i++)
{
lm=i*;
for(int j=;j<=lm;j++)
for(int k=;k<=sx;k++)
for(int l=;l<k;l++)
for(int p=;p<=&&j-p>=;p++)
f[i][j][k][l]+=f[i-][j-p][k][((l-p*pw(i-))%k+k)%k];
// 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]); }
}
void chl()
{
lmm=;
while(tmp)
{
a[++lmm]=tmp%;
tmp/=;
}
}
ll calc()
{
int t=;
ll cnt=,lj=;
for(int i=lmm;i;i--)
{
int ll=a[i];if(i==)ll++;
for(int p=;p<ll;p++)
for(int s=max(,t+p);s<=sx;s++)
cnt+=f[i-][s-t-p][s][(s-(lj+p*pw(i-))%s)%s];
t+=a[i];lj+=a[i]*pw(i-);
}
return cnt;
}
int main()
{
scanf("%lld",&tmp);tmp--;
pre();
chl();
cnt1=calc();
// printf("(%lld)\n",cnt1);
scanf("%lld",&tmp);
chl();
cnt2=calc();
// printf("(%lld)\n",cnt2);
printf("%lld",cnt2-cnt1);
return ;
}
MLE+TLE
在bzoj 50s 下应该还不会超时,考虑解决MLE。
2.发现pre转移的时候,模数s是固定的,即每个状态由模数相同的状态转移过来。
所以只要把模数s的枚举放在最外面,就可以省掉这一维,代价是每一个s都要预处理一遍。
尽可能优化一点。
1)发现L和R的预处理有一部分是重复的,所以把R - L这一部分也在循环里实现而不是最后再减;
2)(微弱)其实预处理到lm[1]-1位就行了。
bzoj上能过了(30s+),洛谷上超时一个点,而且其他点好慢……
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
ll f[][][],l,r,mul[],ans;
int lm[],a[][],sx;
void pw()
{
mul[]=;
for(int i=;i<=lm[];i++)
mul[i]=mul[i-]*;
}
void pre(int s)
{
memset(f,,sizeof f);
f[][][]=;//
int lmm=;
for(int i=;i<=lm[];i++)
{
lmm=min(s,i*);
for(int j=;j<=lmm;j++)
for(int l=;l<s;l++)//l<j是错的(为何)
for(int p=;p<=&&j-p>=;p++)
f[i][j][l]+=f[i-][j-p][((l-p*mul[i-])%s+s)%s];//考虑l-p*pw(i-1)<0
}
}
void chl(int k,ll tmp)
{
while(tmp)
{
a[k][++lm[k]]=tmp%;
tmp/=;
}
}
ll calc(int k,int s)
{
ll cnt=;
// printf("s=%d\n",s);
int t=;ll lj=;
for(int i=lm[k];i;i--)
{
int llm=a[k][i];if(i==)llm++;
for(int p=;p<llm&&s-t-p>=;p++)
cnt+=f[i-][s-t-p][(s-(lj+p*mul[i-])%s)%s];//考虑lj+p*pw(i-1)==0
t+=a[k][i];lj+=a[k][i]*mul[i-];
// printf("i=%d cnt=%d\n",i,cnt);
}
return cnt;
}
int main()
{
scanf("%lld%lld",&l,&r);
chl(,l-);chl(,r);
sx=(lm[]-)*+a[][lm[]];pw();
for(int s=;s<=sx;s++)
{
pre(s);
ans+=calc(,s)-calc(,s);
}
printf("%lld",ans);
return ;
}
TLE
3.发现大家写的都是记忆化深搜。
为什么比较快呢?因为它不需要把一些用不到的状态也预处理出来。
但具体是什么我也不清楚。详见代码?
然后发现大牛的代码:写得好好!于是近乎抄了下来。
这里只要有最后那个d=0时的限制就不用记录各位和为j了,只要最后整个各位和等于s就行。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
ll input()
{
int f=;ll s=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){s=s*+ch-'';ch=getchar();}
return s*f;
}
const int N=,M=;
ll x,y,dp[][M][N][N];
int cnt,a[M],dfn[][M][N][N],tot,mod;
ll calc(bool p,int d,int s,int m)//p为有无限制 ,d是第几位,s是模数也是各位和 ,m是前面填好的东西%s余数
{
if(!d)return (!s&&!m);//初值,第0位时仅当s为0(个位填好后整个的各位和为s)、m为0(整除)时为1
if(dfn[p][d][s][m]==tot)return dp[p][d][s][m];
dfn[p][d][s][m]=tot;ll tmp=;
int l=max(,s-*(d-)),r=min(((p)?:a[d]),s);//r<=s
for(int i=l;i<=r;i++)tmp+=calc(p|(i<a[d]),d-,s-i,(m*+i)%mod);//i是当前位填几
return dp[p][d][s][m]=tmp;
}
ll solve(ll ret)
{
for(cnt=;ret;ret/=)a[++cnt]=ret%;
ll ans=;int sx=cnt*;
for(mod=;mod<=sx;mod++)tot++,ans+=calc(,cnt,mod,);
return ans;
}
int main()
{
x=input();y=input();
return printf("%lld",solve(y)-solve(x-)),;
}
有点奇怪的是下面这份代码数组范围应该都够,但仅改数组范围就能修复一个RE(变成上边那个TLE),也就是说数组越界?
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
ll f[][][],l,r,mul[],ans;
int lm[],a[][],sx;
void pw()
{
mul[]=;
for(int i=;i<=lm[];i++)
mul[i]=mul[i-]*;
}
void pre(int s)
{
memset(f,,sizeof f);
f[][][]=;//
int lmm=;
for(int i=;i<=lm[];i++)
{
lmm=min(s,i*);
for(int j=;j<=lmm;j++)
for(int l=;l<s;l++)//l<j是错的(为何)
for(int p=;p<=&&j-p>=;p++)
f[i][j][l]+=f[i-][j-p][((l-p*mul[i-])%s+s)%s];//考虑l-p*pw(i-1)<0
}
}
void chl(int k,ll tmp)
{
while(tmp)
{
a[k][++lm[k]]=tmp%;
tmp/=;
}
}
ll calc(int k,int s)
{
ll cnt=;
// printf("s=%d\n",s);
int t=;ll lj=;
for(int i=lm[k];i;i--)
{
int llm=a[k][i];if(i==)llm++;
for(int p=;p<llm&&s-t-p>=;p++)
cnt+=f[i-][s-t-p][(s-(lj+p*mul[i-])%s)%s];//考虑lj+p*pw(i-1)==0
t+=a[k][i];lj+=a[k][i]*mul[i-];
// printf("i=%d cnt=%d\n",i,cnt);
}
return cnt;
}
int main()
{
scanf("%lld%lld",&l,&r);
chl(,l-);chl(,r);
sx=(lm[]-)*+a[][lm[]];pw();
for(int s=;s<=sx;s++)
{
pre(s);
ans+=calc(,s)-calc(,s);
}
printf("%lld",ans);
return ;
}
一个RE
真是经典。以后也许要回顾回顾了呢。
人家的代码好好……
bzoj1799(洛谷4127)同类分布(月之谜)的更多相关文章
- 洛谷P4127同类分布
传送 我们要在dfs的板子里记录哪些量呢?当前填的所有数的和sum?当前填的数构成的数值all? sum可以留下,数值就扔掉叭.数值最大是1e18,要是留下,在g数组里有一维的大小是1e18.也许可以 ...
- 洛谷 P4127 [AHOI2009]同类分布 解题报告
P4127 [AHOI2009]同类分布 题目描述 给出两个数\(a,b\),求出\([a,b]\)中各位数字之和能整除原数的数的个数. 说明 对于所有的数据,\(1 ≤ a ≤ b ≤ 10^{18 ...
- 【BZOJ1799】[AHOI2009]同类分布(动态规划)
[BZOJ1799][AHOI2009]同类分布(动态规划) 题面 BZOJ 洛谷 题解 很容易想到数位\(dp\),然而数字和整除原数似乎不好记录.没关系,直接枚举数字和就好了,这样子就可以把整除原 ...
- [BZOJ1799][AHOI2009]同类分布(数位DP)
1799: [Ahoi2009]self 同类分布 Time Limit: 50 Sec Memory Limit: 64 MBSubmit: 1635 Solved: 728[Submit][S ...
- BZOJ1799 self 同类分布 数位dp
BZOJ1799self 同类分布 去博客园看该题解 题意 给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数. [约束条件]1 ≤ a ≤ b ≤ 10^18 题解 1.所有的位数之和&l ...
- 洛谷P2024 食物链
挺神奇 题目描述 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A 吃 B,B 吃 C,C 吃 A. 现有 N 个动物,以 1 - N 编号.每个动物都是 A,B,C 中的一种 ...
- 洛谷OJ P1196 银河英雄传说(带权并查集)
题目描述 公元五八○一年,地球居民迁移至金牛座α第二行星,在那里发表银河联邦 创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展. 宇宙历七九九年,银河系的两大军事集团在巴米利恩星域爆发战争.泰山 ...
- [洛谷P1196][NOI2002]银河英雄传说 - 带偏移量的并查集(1)
Description 公元五八〇一年,地球居民迁至金牛座α第二行星,在那里发表银河联邦创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展. 宇宙历七九九年,银河系的两大军事集团在巴米利恩星域爆发 ...
- 洛谷 P2678 跳石头
题目背景 一年一度的"跳石头"比赛又要开始了! 题目描述 这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石.组委会已经选择好了两块岩石作为比赛起点和终点.在起点和终点之间 ...
随机推荐
- dns 服务架构优化 - 百万级并发不是梦 - bind+namedmanager+dnsmasq
bind: DNS服务端. namedmanager: DNS web管理页面. dnsmasq: 并发查询上游dns域名解析. 问题:作为消息推送业务,单台业务机器域名解析并发达到上万次.业务机器集 ...
- CentOS 7中firewall防火墙详解和配置以及切换为iptables防火墙
官方文档介绍地址: https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Security_Gui ...
- 新手也能学会本地调试微信,natapp 官网映射
本地调试微信的新手指引~ 照着配置,一定可以配置成功,实现本地调试微信,公司好几个同事按照我写的步骤,都独立配成功了. 1.首选在natapp注册一个账号,申请免费隧道或者购买隧道,我买了一个月9元的 ...
- JProfiler性能分析
之前已经介绍过如何调试本地的JBoss.现在额外一篇文章关于如何远程调试Tomcat的,其实远程和本地的区别不大,主要区别只是,JProfiler的GUI运行在你本地,而JProfiler的Agent ...
- Spring学习笔记之The IoC container
IoC is also known as dependency injection (DI). 这是一个过程?什么样的过程呢?对象自己定义它的依赖关系,这意味着,那些他们依赖的对象,只能通过构造函数参 ...
- 2.2 C++类的成员变量和成员函数
参考:http://www.weixueyuan.net/view/6334.html 总结: 类成员的声明和定义: 类成员函数的定义分类内定义(内联)和类外定义(可用 inline 关键字 强制转换 ...
- L1-025 正整数A+B
题的目标很简单,就是求两个正整数A和B的和,其中A和B都在区间[1,1000].稍微有点麻烦的是,输入并不保证是两个正整数. 输入格式: 输入在一行给出A和B,其间以空格分开.问题是A和B不一定是满足 ...
- iOS9 http 不能连接的解决办法
iOS9要求App内访问的网络必须使用HTTPS协议.原有的HTTP请求会报错,适配方法如下. 打开TARGETS-Build Phases, 添加New Run Script Phase,代码如下: ...
- Vue 相关难点汇总
1. 父子组件的双向数据绑定,所以在子组件是不允许修改父组件的属性的. // 解决办法 // 在子组件data中定义一个父组件传递过来的副本,再把该副本利用this.$emit("" ...
- clipboard.js 实现web端---> 复制到剪切板功能
package.json 中: 添加clipboard 依赖 "dependencies": { "vue": "^2.2.2", &quo ...