从数位dp只会windy数变成什么都不会 从只会写递推变成只会写记忆化搜索…
大概就是解决一些对数位有要求而且上下限特别的大…一般有两种实现方法,递推(dp,比较好理解一般都是先学这一种)/记忆化搜索(暴力,方便,容易写,sb方法)
https://www.luogu.org/blog/mak2333/shuwei
https://loj.ac/problem/10163
几个次幂相加,等于转到b进制时那一位为1。转成多叉树的形式对每一棵含有答案的子树乱搞。注意对应的次幂
分析&论文:https://wenku.baidu.com/view/d2414ffe04a1b0717fd5dda8.html
#include
#include
#include
using namespace std;
int f[32][32],a[32];//f:有i层(有i位)有j个1的个数 (整数次幂相加 对应的b进制数位上应为1)
int k,b;
int solve(int x)
{
int len=0,kk=k;
while (x!=0)
{
a[len++]=x%b; //注意对应次幂
x/=b;
}
int ans=0; len--;
for (int i=len;i>=0;i--)
{
if (a[i]==1)
{
ans+=f[i][kk];
kk--;
}
else if (a[i]>1)
{
ans+=f[i+1][kk];
break;
}
if (kk<0) return ans;
}
if (kk==0) ans++; //x刚好也是k个1
return ans;
}
int main()
{
for (int i=0;i<31;i++)
{
f[i][0]=1;
for (int j=1;j<=i;j++) f[i][j]=f[i-1][j]+f[i-1][j-1];
}
int x,y;
scanf("%d%d%d%d",&x,&y,&k,&b);
printf("%d\n",solve(y)-solve(x-1));
return 0;
}
https://loj.ac/problem/10164
//递推
#include
#include
#include
using namespace std;
int a[32],f[32][32];//f[i位][开始的数字](递推用开始的数 记忆化搜索用最后一位数?)
int solve(int x)
{
int len=0; memset(a,0,sizeof(a));
while (x)
{
a[++len]=x%10;
x/=10;
}
int ans=0;
for (int i=len;i;i--)
{
if (a[i+1]>a[i]) break;
for (int j=a[i+1];j
//记忆化搜索
#include
#include
#include
using namespace std;
int f[32][32],a[32];//f[第i高位][填的数字]
int dfs(int len,int st,int lim)
{
if (len==0) return 1;
if (lim==0&&f[len][st]!=-1) return f[len][st];
int ans=0,ed; if (lim==1) ed=a[len]; else ed=9;
for (int i=st;i<=ed;i++)
{
if (lim==1&&i==ed) ans+=dfs(len-1,i,1);
else ans+=dfs(len-1,i,0);
}
return ans;
}
int solve(int x)
{
memset(f,-1,sizeof(f));
int len=0;
while (x)
{
a[++len]=x%10;
x/=10;
}
return dfs(len,0,1);
}
int main()
{
int x,y;
while (scanf("%d%d",&x,&y)!=EOF)
{
printf("%d\n",solve(y)-solve(x-1));
}
return 0;
}
https://loj.ac/problem/10165
//递推
#include
#include
#include
#include
using namespace std;
int f[32][32],a[32]; //f[位数i][第i位(最高位)的数字]
void init()
{
for (int i=0;i<=9;i++) f[1][i]=1;
for (int i=2;i<=31;i++)
for (int j=0;j<=9;j++)
for (int k=0;k<=9;k++)
if (abs(j-k)>=2) f[i][j]+=f[i-1][k];
}
int solve(int x)
{
memset(a,0,sizeof(a));
int len=0;
while (x)
{
a[++len]=x%10;
x/=10;
}
int ans=0;
for (int i=1;i=2) ans+=f[i][j]; //跟前一位比较
if (abs(a[i+1]-a[i])<2) break;
if (i==1) ans++;
}
return ans;
}
int main()
{
init();
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",solve(b)-solve(a-1));
return 0;
}
//记忆化搜索
#include
#include
#include
using namespace std;
int f[10][10],a[10];//f[第i高位][填的数字]
int dfs(int len,int st,int ze,int lim)//ze是否有前导零 lim:是否达到上限
{
if (len==0) return 1;
if (lim==0&&f[len][st]!=-1) return f[len][st];
int ans=0,ed;
if (lim==1) ed=a[len]; else ed=9;
for (int i=0;i<=ed;i++)
{
if (ze==1)
{
int zz=0;
if (i==0) zz=1;
if (lim==1&&i==ed) ans+=dfs(len-1,i,zz,1);
else ans+=dfs(len-1,i,zz,0);
}
else if (abs(st-i)>=2)
{
if (lim==1&&i==ed) ans+=dfs(len-1,i,0,1);
else ans+=dfs(len-1,i,0,0);
}
}
if (lim==0&&st!=0) f[len][st]=ans;
return ans;
}
int solve(int x)
{
memset(a,0,sizeof(a));
int len=0;
while (x)
{
a[++len]=x%10;
x/=10;
}
return dfs(len,0,1,1);
}
int main()
{
memset(f,-1,sizeof(f));
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",solve(b)-solve(a-1));
return 0;
}
https://loj.ac/problem/10166
不要想太多,直接爆搜就好
#include
#include
#include
using namespace std;
int f[32][110],a[32],n;//f[位数][余数]
int dfs(int len,int mod,int lim)
{
if (len==0)
{
if (mod==0) return 1; else return 0;
}
if (f[len][mod]!=-1&&lim==0) return f[len][mod];
int ans=0,ed;
if (lim==1) ed=a[len]; else ed=9;
for (int i=0;i<=ed;i++)
{
if (lim==1&&i==ed) ans+=dfs(len-1,(mod+i)%n,1);
else ans+=dfs(len-1,(mod+i)%n,0);
}
if (lim==0) f[len][mod]=ans;
return ans;
}
int solve(int x)
{
memset(f,-1,sizeof(f));
memset(a,0,sizeof(a));
int len=0;
while (x)
{
a[++len]=x%10;
x/=10;
}
return dfs(len,0,1);
}
int main()
{
int a,b;
while (scanf("%d%d%d",&a,&b,&n)!=EOF)
printf("%d\n",solve(b)-solve(a-1));
return 0;
}
https://loj.ac/problem/10167
#include
#include
#include
using namespace std;
int f[7][10],a[7];//f[位数i][第i位数字]
int dfs(int len,int last,int lim)
{
if (len==0) return 1;
if (lim==0&&f[len][last]!=-1) return f[len][last];
int ans=0,ed;
if (lim==1) ed=a[len]; else ed=9;
for (int i=0;i<=ed;i++)
{
if (i==4) continue;
if (last==6&&i==2) continue;
if (lim==1&&i==ed) ans+=dfs(len-1,i,1);
else ans+=dfs(len-1,i,0);
}
if (lim==0) f[len][last]=ans;
return ans;
}
int solve(int x)
{
memset(f,-1,sizeof(f));
memset(a,0,sizeof(a));
int len=0;
while (x)
{
a[++len]=x%10;
x/=10;
}
return dfs(len,0,1);
}
int main()
{
int a,b;
while (scanf("%d%d",&a,&b)!=EOF&&a&&b)
{
printf("%d\n",solve(b)-solve(a-1));
}
return 0;
}
https://loj.ac/problem/10168
完全平方公式2333(没想到 装傻.jpg)
题解&分析:https://blog.csdn.net/ten_three/article/details/19698055
&https://www.cnblogs.com/kuangbin/archive/2013/05/01/3053233.html
#include
#include
#include
using namespace std;
#define LL long long
#define mod 1000000007
struct node
{
LL s,sum,sqrsum;//个数N 各数之和 各数平方和
node()
{
s=sum=sqrsum=-1;
}
}f[20][10][10];//f[位数][各数位之和mod7][这个数mod7]
int a[20];
LL pi[20];
node dfs(int len,int s,int qs,int lim)//数位 各数位之和mod7 这个数mod7 是否上限
{
if (len==0)
{
node tt;
tt.s=tt.sum=tt.sqrsum=0;
if (s!=0&&qs!=0) tt.s=1;
return tt;
}
if (lim==0&&f[len][s][qs].sqrsum!=-1) return f[len][s][qs];
node ans; ans.s=ans.sum=ans.sqrsum=0;
int ed=0;
if (lim==1) ed=a[len]; else ed=9;
for (int i=0;i<=ed;i++)
{
if (i==7) continue;
/*
第len位填i f1,f2,f2...fN记录前len位已经填好的数字
对答案的贡献为: (f1+ i*10^(len-1) )^2+(f2+ i*10^(len-1) )^2+...+(fN+ i*10^(len-1) )^2
将平方和展开 整理 提公因式:
N*(i*10^(len-1))^2+ f1^2+f2^2+...+fN^2+ 2*(i*10^(len-1))*(f1+f2+...fN)
其中 N即为要求的s 2*(i*10^(len-1))*(f1+f2+...fN) 即为要求的sum 整一个即为sqrsum
*/
node tt;
if (lim==1&&i==ed) tt=dfs(len-1,(s+i)%7,(qs*10+i)%7,1);
else tt=dfs(len-1,(s+i)%7,(qs*10+i)%7,0);
LL si=(i*pi[len-1])%mod;
ans.s=(ans.s+tt.s)%mod;
ans.sum=(ans.sum+(tt.sum+ (si*tt.s)%mod ) %mod )%mod;
ans.sqrsum=(ans.sqrsum+tt.sqrsum+( ((si*si)%mod*tt.s)%mod+ ((2*si)%mod *tt.sum) %mod )%mod )%mod;
}
if (lim==0) f[len][s][qs]=ans;
return ans;
}
LL solve(LL x)
{
memset(a,0,sizeof(a));
int len=0;
while (x)
{
a[++len]=x%10;
x/=10;
}
node ans=dfs(len,0,0,1);
return ans.sqrsum%mod;
}
int main()
{
pi[0]=1;
for (int i=1;i<=18;i++) pi[i]=pi[i-1]*10;
int t;
scanf("%d",&t);
while (t--)
{
LL x,y;
scanf("%lld%lld",&x,&y);
if (x>y) swap(x,y);
printf("%lld\n",(solve(y)-solve(x-1)+mod)%mod);
}
return 0;
}
https://loj.ac/problem/10169
别人都是三维f[数位][ ][前导零]+做10次,每次找一个数
只有我是f[数位][填的数字]鬼畜结构体一次找完所有数字且不带前导零……
所以注意前导零!!!具体看代码里解释的!
不加前导零那一维就要记得预处理
#include
#include
#include
using namespace std;
struct node
{
bool bk;
long long a[10];
node()
{
bk=0; memset(a,-1,sizeof(a));
}
}f[15][10];
int a[15];
long long pi[15],c[15];
node jia(node x,node y)
{
for (int i=0;i<=9;i++) x.a[i]+=y.a[i];
return x;
}
node dfs(int len,int x,int ze,int lim) //ze:zero是否有前导零
{
if (len==0) {node tt;tt.bk=1;memset(tt.a,0,sizeof(tt.a));tt.a[x]=1;return tt;}
if (ze==0&&lim==0&&f[len][x].bk==1) return f[len][x];
node ans; ans.bk=1; memset(ans.a,0,sizeof(ans.a));
int ed;
if (lim==1) ed=a[len]; else ed=9;
for (int i=0;i<=ed;i++)
{
node tt;
tt=dfs(len-1,i,i==0&&ze==1,i==ed&&lim==1);
/*if (ze==1&&i==0) ;
else if (len>1) tt.a[i]+=tt.t;*/
if (ze==1&&i==0) ;
else
{
if (len>1)
{
if (lim==1&&i==ed) tt.a[i]+=c[len-1]+1;
else tt.a[i]+=pi[len-1];
}
}
ans=jia(ans,tt);
}
if (ze==0&&lim==0) f[len][x]=ans;
//关于前导零不能用/存答案 例1010 当len=3,往下找len=2的情况时 由于此时f[2][ ].a[0]=9 (10,20,30,...,90) 而答案应为18 (10,20,...,90; 01,02,...,09)
return ans;
}
node solve(long long x)
{
memset(a,0,sizeof(a));
memset(c,0,sizeof(c));
/*for (int i=0;i<15;i++)
for (int j=0;j<10;j++) memset(f[i][j].a,-1,sizeof(f[i][j].a)),f[i][j].bk=0;*/
int len=0;
while (x)
{
a[++len]=x%10;
x/=10;
c[len]=a[len]*pi[len-1]+c[len-1]; //预处理最多有多少个数在这里面(当前枚举位出现次数)
}
return dfs(len,0,1,1);
}
int main()
{
pi[0]=1;
for (int i=1;i<15;i++) pi[i]=pi[i-1]*10;
long long x,y;
scanf("%lld%lld",&x,&y);
node r=solve(y),l=solve(x-1);
for (int i=0;i<=9;i++)
{
printf("%lld ",r.a[i]-l.a[i]);
}
printf("\n");
return 0;
}
完结撒花!
(sd记忆化搜索真好用)