今天突然做到一个数位dp的题目,才发现一直没有数位dp的板子。
在找数位dp的题目的时候,发现我的树形dp大概也需要整理一下了?
一、不要62 HDU - 2089
题意:
某个区间中,数字中不含有4或者62数有多少个。
题解:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
using namespace std;
int dp[20][2];int x[20];
int dfs(int pos,int pre,int sta,bool limit)
{
int sum=0;
if(pos==-1) return 1;
if(!limit&&dp[pos][sta]!=-1) return dp[pos][sta];
int up=(limit?x[pos]:9);
for(int i=0;i<=up;i++)
{
if(pre==6&&i==2) continue;
if(i==4) continue;
sum+=dfs(pos-1,i,i==6,limit&&i==x[pos]);
}
if(!limit) dp[pos][sta]=sum;
return sum;
}
int f(int t)
{
int pos=0;
while(t)
{
x[pos++]=t%10;
t/=10;
}
return dfs(pos-1,-1,0,true);
}
int main(void)
{
int n,m;
memset(dp,-1,sizeof(dp));
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==0&&m==0) break;
int ans=f(m)-f(n-1);
cout<<ans<<endl;
}
return 0;
}
二、F(x) HDU - 4734
题意:对于一个有n位(这n位从高位到低位分别是An An-1 An-2 … A2 A1 )的十进制数,我们定义它的权值F(x)=An * 2n-1 + An-1 * 2n-2 + … + A2 * 2 + A1 * 1.现在给你两个数A,B,请计算[0,B]范围内有多少个权值<=F(A)的数
题解:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define llu unsigned ll
using namespace std;
int dp[20][11000];
int x[20];
int n,m;
int F(int x)
{
int p=1;
int ans=0;
while(x)
{
ans+=p*(x%10);
x/=10;
p<<=1;
}
return ans;
}
int dfs(int pos,int sta,bool limit)
{
if(sta<0) return 0;
if(pos<0) return sta>=0;
if(!limit&&dp[pos][sta]!=-1) return dp[pos][sta];
int up=limit?x[pos]:9;
int ans=0;
for(int i=0;i<=up;i++)
{
ans+=dfs(pos-1,sta-i*(1<<pos),limit&&i==up);
}
if(!limit) dp[pos][sta]=ans;
return ans;
}
int f(int t)
{
int pos=0;
while(t)
{
x[pos++]=t%10;
t/=10;
}
return dfs(pos-1,F(n),true);
}
int main(void)
{
int t;
int pp=0;
memset(dp,-1,sizeof(dp));
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
printf("Case #%d: %d\n",++pp,f(m));
}
}
三、 B-number HDU - 3652
题意:0–n中含有数字13且能被13整除的数有多少个。
题解:
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<vector>
#include<algorithm>
#define ll long long
#define llu unsigned ll
using namespace std;
int dp[20][2][2][15];
int x[20];
int dfs(int pos,int last,bool pre,bool sta,bool limit)
{
int sum=0;
if(pos==-1)
{
if(sta&&last==0) return 1;
else return 0;
}
if(!limit&&dp[pos][pre][sta][last]!=-1) return dp[pos][pre][sta][last];
int up=limit?x[pos]:9;
for(int i=0;i<=up;i++)
sum+=dfs(pos-1,(last*10+i)%13,i==1,sta||pre&&i==3,limit&&i==up);
if(!limit) dp[pos][pre][sta][last]=sum;
return sum;
}
int f(int t)
{
int pos=0;
while(t)
{
x[pos++]=t%10;
t/=10;
}
return dfs(pos-1,0,false,false,true);
}
int main(void)
{
int n;
memset(dp,-1,sizeof(dp));
while(scanf("%d",&n)!=EOF)
{
printf("%d\n",f(n));
}
return 0;
}
四、Balanced Number HDU - 3709
题意:
一个数字是平衡数字,当且仅当存在它的一个数位作为平衡点,这个点左边和右边【数字*到这个点的距离】之和相等
题解:
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<vector>
#include<algorithm>
#define ll long long
#define llu unsigned ll
using namespace std;
int x[20];
ll dp[20][20][4000];
ll dfs(int pos,int p,int num,bool limit)
{
ll sum=0;
if(pos==-1)
{
if(num==0) return 1;
else return 0;
}
if(num<0) return 0;
if(!limit&&dp[pos][p][num]!=-1) return dp[pos][p][num];
int up=limit?x[pos]:9;
for(int i=0;i<=up;i++)
sum+=dfs(pos-1,p,num+(pos-p)*i,limit&&i==up);
if(!limit) dp[pos][p][num]=sum;
return sum;
}
ll f(ll t)
{
int pos=0;
if(t<0) return 0;
while(t)
{
x[pos++]=t%10;
t/=10;
}
ll sum=0;
for(int i=0;i<pos;i++)
sum+=dfs(pos-1,i,0,true);
return sum-pos+1;
}
int main(void)
{
int t;
scanf("%d",&t);
memset(dp,-1,sizeof(dp));
while(t--)
{
ll x,y;
scanf("%lld%lld",&x,&y);
printf("%lld\n",f(y)-f(x-1));
}
return 0;
}
五、吉哥系列故事——恨7不成妻 HDU - 4507
题意:
如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关——
1、整数中某一位是7;
2、整数的每一位加起来的和是7的整数倍;
3、这个整数是7的整数倍;
现在问题来了:吉哥想知道在一定区间内和7无关的数字的平方和。
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<vector>
#include<algorithm>
#define ll long long
#define llu unsigned ll
using namespace std;
const int mod=1e9+7;
int x[20];
ll dp[20][10][10];
ll cnt[20][10][10];
ll sum[20][10][10];
ll p[20];
ll dfs(int pos,int add,int div,bool limit,ll &ss,ll &dd,ll &cc)
{
if(pos==-1)
{
dd=0,ss=0;
if(add!=0&&div!=0) cc=1;
else cc=0;
return dd;
}
if(!limit&&dp[pos][add][div]!=-1)
{
ss=sum[pos][add][div];
dd=dp[pos][add][div];
cc=cnt[pos][add][div];
return dp[pos][add][div];
}
int up=limit?x[pos]:9;
ss=0,dd=0,cc=0;
ll s,d,c;
for(int i=0;i<=up;i++)
{
if(i==7) continue;
dfs(pos-1,(add+i)%7,(div*10+i)%7,limit&&i==up,s,d,c);
ll k=i*p[pos]%mod;
cc=(cc+c)%mod;
ss=(((c*k%mod)+s)%mod+ss)%mod;
dd=((c*k%mod*k%mod)%mod+d%mod+2*k*s%mod+dd%mod)%mod;
}
if(!limit)
{
sum[pos][add][div]=ss;
cnt[pos][add][div]=cc;
dp[pos][add][div]=dd;
}
return dd;
}
ll f(ll t)
{
int pos=0;
while(t)
{
x[pos++]=t%10;
t/=10;
}
ll s,d,c;
return dfs(pos-1,0,0,true,s,d,c);
}
int main(void)
{
int t;
scanf("%d",&t);
memset(dp,-1,sizeof(dp));
p[0]=1;
for(int i=1;i<20;i++)
p[i]=p[i-1]*10%mod;
while(t--)
{
ll x,y;
scanf("%lld%lld",&x,&y);
printf("%lld\n",((f(y)-f(x-1))%mod+mod)%mod);
}
return 0;
}
六、Balanced Numbers SPOJ - BALNUM
题意:
求区间内数字满足“奇数各数出现偶数次,偶数各数出现奇数次”的数字的个数
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<vector>
#define ll long long
using namespace std;
int x[20];
ll dp[20][60005];
ll cm[11];
bool check(int sta)
{
for(int i=0;i<=9;sta/=3,i++)
{
if((i&1)&&sta%3==1) return false;
if(!(i&1)&&sta%3==2) return false;
}
return true;
}
int change(int sta,int i)
{
int tt=(sta%cm[i+1])/cm[i];
if(tt==2) return sta-cm[i];
else return sta+cm[i];
}
ll dfs(int pos,int sta,bool pre,bool limit)
{
if(pos==-1)
{
if(check(sta)) return 1;
else return 0;
}
if(!pre&&!limit&&dp[pos][sta]!=-1) return dp[pos][sta];
ll ans=0;
int up=limit?x[pos]:9;
for(int i=0;i<=up;i++)
if(pre)
ans+=dfs(pos-1,i==0?0:change(sta,i),i==0,limit&&i==x[pos]);
else ans+=dfs(pos-1,change(sta,i),pre,limit&&i==x[pos]);
if(!pre&&!limit) dp[pos][sta]=ans;
return ans;
}
ll f(ll t)
{
int pos=0;
while(t)
{
x[pos++]=t%10;
t/=10;
}
return dfs(pos-1,0,true,true);
}
int main(void)
{
int t;
scanf("%d",&t);
memset(dp,-1,sizeof(dp));
cm[0]=1;
for(int i=1;i<11;i++)
cm[i]=cm[i-1]*3;
while(t--)
{
ll x,y;
scanf("%lld%lld",&x,&y);
printf("%lld\n",f(y)-f(x-1));
}
return 0;
}
七、Round Numbers POJ - 3252
题意:一个数x是round number,要求x写成二进制形式的时候数位上0的个数要大于等于1的个数,题目要求求出给定范围内的round number个数。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#define ll long long
using namespace std;
const int maxn=40;
int dp[maxn][maxn][maxn];
int x[maxn];
int dfs(int pos,int num0,int num1,bool pre,bool limit)
{
int sum=0;
if(pos==-1)
{
if(num0>=num1) return 1;
else return 0;
}
if(!pre&&!limit&&dp[pos][num0][num1]!=-1) return dp[pos][num0][num1];
int up=(limit?x[pos]:1);
for(int i=0;i<=up;i++)
{
if(pre)
sum+=dfs(pos-1,num0,i==1?num1+1:num1,i==0,limit&&i==up);
else
sum+=dfs(pos-1,i==0?num0+1:num0,i==1?num1+1:num1,pre,limit&&i==up);
}
if(!limit&&!pre) dp[pos][num0][num1]=sum;
return sum;
}
int f(int t)
{
ll pos=0;
while(t)
{
x[pos++]=t%2;
t/=2;
}
return dfs(pos-1,0,0,true,true);
}
int main(void)
{
int n,m;
memset(dp,-1,sizeof(dp));
int t;
while(scanf("%d%d",&n,&m)!=EOF)
{
int ans=f(m)-f(n-1);
printf("%d\n",ans);
}
return 0;
}
八、Beautiful numbers CodeForces - 55D
题意:有T组询问,每次询问区间[l, r]中的beautiful number有多少。beautiful number是指这个数可以被组成它的数字整除。
题解:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#define ll long long
#define llu unsigned ll
using namespace std;
ll dp[20][50][2530];
int ha[2530];
int x[20];
int cnt=0;
int max_lcm;
ll gcd(ll a,ll b)
{
if(b==0) return a;
return gcd(b,a%b);
}
ll lcm(ll a,ll b)
{
return a/gcd(a,b)*b;
}
void dfs(int now,int res)
{
if(now==10)
{
ha[res]=1;
return ;
}
dfs(now+1,lcm(res,now));
dfs(now+1,res);
}
ll dfs(int pos,int now_lcm,int now_last,bool limit)
{
ll sum=0;
if(pos==-1)
{
if(now_last%now_lcm==0) return 1;
else return 0;
}
if(!limit&&dp[pos][ha[now_lcm]][now_last]!=-1) return dp[pos][ha[now_lcm]][now_last];
int up=limit?x[pos]:9;
for(int i=0;i<=up;i++)
{
sum+=dfs(pos-1,i==0?now_lcm:lcm(now_lcm,i),(now_last*10+i)%max_lcm,limit&&i==up);
}
if(!limit)
dp[pos][ha[now_lcm]][now_last]=sum;
return sum;
}
ll f(ll t)
{
int pos=0;
while(t)
{
x[pos++]=t%10;
t/=10;
}
return dfs(pos-1,1,0,true);
}
int main(void)
{
ll x,y;
dfs(1,1);
for(int i=1;i<2530;i++)
{
if(ha[i]) ha[i]=++cnt;
}
max_lcm=1;
for(int i=1;i<=9;i++)
max_lcm=lcm(max_lcm,i);
//cout<<max_lcm<<endl;
//cout<<cnt<<endl;
memset(dp,-1,sizeof(dp));
int t;
scanf("%d",&t);
while(t--)
{
scanf("%I64d%I64d",&x,&y);
printf("%I64d\n",f(y)-f(x-1));
}
return 0;
}
九、Classy Numbers CodeForces - 1036C
题意:所有数位上不为0的数不超过3个
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#define ll long long
#define llu unsigned ll
using namespace std;
int dp[20][4];int x[20];
ll dfs(int pos,int pre,int sta,bool limit)
{
ll sum=0;
if(pos==-1) return 1;
if(!limit&&dp[pos][sta]!=-1) return dp[pos][sta];
if(sta>=3) return dp[pos][sta]=1;
int up=(limit?x[pos]:9);
for(int i=0;i<=up;i++)
{
sum+=dfs(pos-1,i,i==0?sta:sta+1,limit&&i==up);
}
if(!limit) dp[pos][sta]=sum;
return sum;
}
ll f(ll t)
{
int pos=0;
while(t)
{
x[pos++]=t%10;
t/=10;
}
return dfs(pos-1,-1,0,true);
}
int main(void)
{
ll n,m;
int t;
memset(dp,-1,sizeof(dp));
scanf("%d",&t);
while(t--)
{
scanf("%lld%lld",&n,&m);
ll ans=f(m)-f(n-1);
printf("%lld\n",ans);
}
return 0;
}
十、 Bomb HDU - 3555
题意:包含49这个数的数字个数。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#define ll long long
using namespace std;
ll dp[28][2][2];
ll x[20];
ll dfs(ll pos,ll pre,ll sta,bool limit)
{
ll sum=0;
if(pos==-1)
{
if(sta) return 1;
else return 0;
}
if(!limit&&dp[pos][pre][sta]!=-1) return dp[pos][pre][sta];
int up=(limit?x[pos]:9);
for(int i=0;i<=up;i++)
{
sum+=dfs(pos-1,i==4,sta||pre&&i==9,limit&&i==up);
}
if(!limit) dp[pos][pre][sta]=sum;
return sum;
}
ll f(ll t)
{
ll pos=0;
while(t)
{
x[pos++]=t%10;
t/=10;
}
return dfs(pos-1,0,0,true);
}
int main(void)
{
ll n,m;
memset(dp,-1,sizeof(dp));
int t;
scanf("%d",&t);
while(t--)
{
scanf("%lld",&n);
ll ans=f(n);
printf("%lld\n",ans);
}
return 0;
}