数位DP|记忆化搜索
听隔壁巨佬说DP和记搜可以互相转换
显然这个题是可以用记忆化搜索过的,那我们应传哪几个参数?
首先就是记搜最基本的位置标记。
然后就是枚举的数字各位之和,以及取模之后的余数(判断能否整除某个数)。
最后就是判边界的参数。
当我们搜到最后一位时如果余数为0,并且各位之和=mod,那就直接返回1,否则返回零。
显然我们把这几个数装进数组里,是开不下的。
long long 为8字节 \(8\times 20\times 200\times 200\times 200 \div1024 \div1024 =1220.703125 MB8×20×200×200×200÷1024÷1024=1220.703125MB\)
所以我们考虑枚举模数(各位之和)mod,就要求n数字各位之和=mod,n%mod=0,。
因为每个位上最大是9,所以我们从1枚举到\(len \times× 9\).这样我们就将所有情况(数字各位之和)都考虑了进去。
这样我们就省掉了DP数组中模数那一维,妥妥的能开下。(〃‘▽‘〃)
我们将相同的状态用数组记录,这样在下一次搜到该状态时就能直接调用啦~~
然后分享几个卡常技巧。
今天做了一道和这个题类似的毒瘤卡常题,代码十几分钟,卡常卡了两小时QAQ,莫非真的是人丑常数大。
首先,我们可以剪枝,若sum(数字各位之和>mod),直接return 0,因为我们越往后,sum只会越来越大,不可能等于mod。
然后就是清空DP数组时将memset改为for循环,我们只用将我们上一次用到的数组清空(1~枚举的mod)就可以了(实测跑的飞快)
再然后就是搜索,正常情况下我们是这样写的:
cin>>a>>b;cout<<calc(b)-calc(a-1);
但是我们忽视了一个问题,那就是相同的mod我们应该只记搜一次,但因为我们分开来计算,所以就算了两次,常数巨大>_<
那么我们就在计算b的答案的同时顺便减去a的答案就行了,这样我们每个mod只用到了一遍。
最后就是一些基本的卡常操作啦~~
卡常结果:$ 7.58s \rightarrow 3.11s$
还有如果让求C进制下的答案,我们只需要将代码中的10改为C就可以了
最后献上我丑陋的代码
#include<cstdio>
#include<iostream>
using namespace std;
bool ok;
int len,cnt,w[21],ww[71],mod;
long long b,a,dp[21][205][205];
inline long long read()
{
char ch;long long x=0,f=1;
while(!isdigit(ch=getchar()))
{
(ch=='-')&&(f=-f);
}
while(isdigit(ch))
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
inline void write(long long x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
long long dfs(int l,int sum,int m,int g)
{
if(sum>mod)return 0;
if(l==0)
return (m==0&&sum==mod);
register int end=g?w[l]:9;
if(!g&&dp[l][sum][m]!=-1)return dp[l][sum][m];
register long long ans=0;
for(register int i=0;i<=end;++i)
ans+=dfs(l-1,sum+i,(m*10+i)%mod,g&&(i==end));
if(!g)dp[l][sum][m]=ans;
return ans;
}
long long dfs1(int l,int sum,int m,int g)
{
if(sum>mod)return 0;
if(l==0)
return (m==0&&sum==mod);
register int end=g?ww[l]:9;
if(!g&&dp[l][sum][m]!=-1)return dp[l][sum][m];
register long long ans=0;
for(register int i=0;i<=end;++i)
ans+=dfs1(l-1,sum+i,(m*10+i)%mod,g&&(i==end));
if(!g)dp[l][sum][m]=ans;
return ans;
}
inline long long c1(long long x)
{
if(!ok)
{
cnt=0;
while(x)
{
ww[++cnt]=x%10;
x/=10;
}
ok=1;
}
return dfs1(cnt,0,0,1);
}
inline long long c(long long x)
{
int i,j,k;
len=0;
while(x)
{
w[++len]=x%10;
x/=10;
}
register long long ans=0;
for(mod=1;mod<=len*9;++mod)
{
for(i=0;i<=len;++i)
for(j=0;j<=mod;++j)
for(k=0;k<=mod;++k)
dp[i][j][k]=-1;
ans+=dfs(len,0,0,1);
ans-=c1(a-1);
}
return ans;
}
int main()
{
a=read(),b=read();
write(c(b));
return 0;
}
原文地址:https://www.cnblogs.com/wljss/p/11496603.html