数位DP总结
a positive integer number is beautiful if and only if it is divisible by each of its nonzero digits.
问一个区间内[l,r]有多少个Beautiful数字
范围9*10^18
思路一般,但是取模以及映射也是非常玄妙的
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define ll long long //dp[i][j][k][sta] means the first i nomber,whether it has limit,the val%2520,the lcm of ezch number ll pow[22],dp[22][2][2520][50]; int num[22],flcm[512],Map[2520],Map1[50],tmp[22],tot; int gcd(int a,int b) { if(!b) return a; return gcd(b,a%b); } int lcm(int a,int b) { if(a==0) return b; if(b==0) return a; return a*b/gcd(a,b); } void init() { pow[0]=1; for(int i=1;i<=18;i++) { pow[i]=pow[i-1]*10%2520; } for(int i=1;i<(1<<9);i++) for(int j=1;j<=9;j++) if(i&(1<<j-1)) flcm[i]=lcm(flcm[i-(1<<j-1)],j); for(int i=1;i<=(1<<9);i++) if(!Map[flcm[i]]) Map1[++tot]=flcm[i],Map[flcm[i]]=tot; } ll solve(ll x) { int len=0; ll ans=0; while(x) tmp[++len]=x%10,x/=10; for(int i=1;i<=len;i++) num[i]=tmp[len-i+1]; memset(dp,0,sizeof(dp)); dp[0][1][0][1]=1; for(int i=0;i<=len;i++) for(int j=0;j<2;j++) for(int k=0;k<2520;k++) for(int l=1;l<=tot;l++) { ll p=dp[i][j][k][l]; int pp=Map1[l]; if(!p) continue; if (i==len) { if (k % pp==0) ans+=p; continue; } if(!j) { for(int q=0;q<=9;q++) { dp[i+1][0][(k+q*pow[len-i-1])%2520][Map[lcm(pp,q)]]+=p; } }else { for(int q=0;q<num[i+1];q++) { dp[i+1][0][(k+q*pow[len-i-1])%2520][Map[lcm(pp,q)]]+=p; } dp[i+1][1][(k+num[i+1]*pow[len-i-1])%2520][Map[lcm(pp,num[i+1])]]+=p; } } return ans; } int main() { init(); int cas; scanf("%d",&cas); while(cas--) { ll a,b; scanf("%lld %lld",&a,&b); printf("%lld\n",solve(b)-solve(a-1)); } }
杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer)。
杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。
不吉利的数字为所有含有4或62的号码。例如:
62315 73418 88914
都属于不吉利号码。但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列。
你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多少辆新的士车上牌照了。
杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。
不吉利的数字为所有含有4或62的号码。例如:
62315 73418 88914
都属于不吉利号码。但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列。
你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多少辆新的士车上牌照了。
Input输入的都是整数对n、m(0<n≤m<1000000),如果遇到都是0的整数对,则输入结束。
Output对于每个整数对,输出一个不含有不吉利数字的统计个数,该数值占一行位置。
Sample Input
1 100 0 0
Sample Output
80
水体不解释
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int dp[25][2][2],tmp[25],num[25]; int solve(int x) { int len=0,ans=0; while(x) tmp[++len]=x%10,x/=10; for(int i=1;i<=len;i++) num[len-i+1]=tmp[i]; memset(dp,0,sizeof(dp)); dp[0][1][0]=1; for(int i=0;i<=len;i++) for(int j=0;j<2;j++) for(int k=0;k<2;k++) { int pp=dp[i][j][k]; if(!pp) continue; if(i==len) ans+=pp; if(!j) { if(!k) { for(int q=0;q<=9;q++) if(q!=6&&q!=4) dp[i+1][0][0]+=pp;else if(q!=4)dp[i+1][0][1]+=pp; }else for(int q=0;q<=9;q++) if(q!=6&&q!=2&&q!=4) dp[i+1][0][0]+=pp;else if(q==6) dp[i+1][0][1]+=pp; }else { if(!k) { for(int q=0;q<num[i+1];q++) if(q!=6&&q!=4) dp[i+1][0][0]+=pp;else if(q!=4)dp[i+1][0][1]+=pp; if(num[i+1]!=6&&num[i+1]!=4) dp[i+1][1][0]+=pp;else if(num[i+1]!=4)dp[i+1][1][1]+=pp; }else { for(int q=0;q<num[i+1];q++) if(q!=6&&q!=2&&q!=4) dp[i+1][0][0]+=pp;else if(q==6) dp[i+1][0][1]+=pp; if(num[i+1]!=6&&num[i+1]!=2&&num[i+1]!=4) dp[i+1][1][0]+=pp;else if(num[i+1]==6) dp[i+1][1][1]+=pp; } } } return ans; } int main() { int a,b; while(~scanf("%d %d",&a,&b)&&(a||b)) { printf("%d\n",solve(b)-solve(a-1)); } }
还有几道题
C - Bomb
Solution:同上
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #define ll long long using namespace std; ll tmp[55],num[55],len,dp[55][2][2]; ll solve(ll x) { len=0; while(x) { tmp[++len]=x%10; x/=10; } memset(dp,0,sizeof(dp)); for(ll i=1;i<=len;i++) num[len-i+1]=tmp[i]; dp[0][1][0]=1; ll ans=0; for(ll i=0;i<=len;i++) { for(ll j=0;j<2;j++) { for(ll k=0;k<2;k++) { ll pp=dp[i][j][k]; if(i==len) { ans+=pp; continue; } if(!pp) continue; if(!j) { if(!k) { for(ll q=0;q<=9;q++) if(q!=4) dp[i+1][0][0]+=pp;else dp[i+1][0][1]+=pp; }else for(ll q=0;q<=8;q++) { if(q==4) dp[i+1][0][1]+=pp;else dp[i+1][0][0]+=pp; } }else { if(!k) { for(ll q=0;q<num[i+1];q++) { if(q==4) { dp[i+1][0][1]+=pp; }else dp[i+1][0][0]+=pp; } if(num[i+1]==4) dp[i+1][1][1]+=pp;else dp[i+1][1][0]+=pp; }else { for(ll q=0;q<num[i+1];q++) if(q!=9) { if(q==4) { dp[i+1][0][1]+=pp; }else dp[i+1][0][0]+=pp; } if(num[i+1]!=9) { if(num[i+1]==4) { dp[i+1][1][1]+=pp; }else dp[i+1][1][0]+=pp; } } } } } } return ans; } int main() { ll cas,n; scanf("%lld",&cas); while(cas--) { scanf("%lld",&n); printf("%lld\n",n-solve(n)+1); } }
D - Round Numbers
第一个用dfs写的题#include<cstdio> #include<cstring> #define ll long long using namespace std; int dp[40][40][40],num[40]; ll dfs(ll pos,ll num0,ll num1,bool flag,bool z) { if(pos==-1) return num0>=num1; if(!flag&&dp[pos][num0][num1]!=-1) return dp[pos][num0][num1]; ll maxn=flag?num[pos]:1; ll ans=0; for(ll i=0;i<=maxn;i++) ans+=dfs(pos-1,(z&&i==0)?0:num0+(i==0),(z&&i==0)?0:num1+(i==1),flag&&i==maxn,z&&i==0); if(!flag) dp[pos][num0][num1]=ans; return ans; } ll cal(ll x) { ll pos=0; while(x) { num[pos++]=x%2; x/=2; } return dfs(pos-1,0,0,1,1); } int main() { ll n,m; memset(dp,-1,sizeof(dp)); scanf("%lld%lld",&n,&m); printf("%lld\n",cal(m)-cal(n-1)); return 0; }
E - Balanced Number
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #define ll long long using namespace std; ll l,r; ll tmp[25],num[25],f[25][25][2005]; ll dfs(ll pos,ll x,ll sum,ll limit) { if(!pos) return sum==0; if(sum<0) return 0; ll ans=0; if(!limit&&~f[pos][x][sum]) return f[pos][x][sum]; ll up=limit? num[pos]:9; for(ll i=0;i<=up;i++) { ans+=dfs(pos-1,x,sum+(i)*(pos-x),limit&&(i==up)); } if(!limit) f[pos][x][sum]=ans; return ans; } ll solve(ll x) { ll len=0; while(x) { tmp[++len]=x%10; x/=10; } ll sum=0; for(ll i=1;i<=len;i++) num[i]=tmp[i]; for(ll i=1;i<=len;i++) sum+=dfs(len,i,0,1); return sum-len+1; } int main() { ll T; memset(f,-1,sizeof(f)); scanf("%lld",&T); while(T--) { scanf("%lld %lld",&l,&r); printf("%lld\n",solve(r)-solve(l-1)); } }
bzoj1388
一不小心先打成爆艘了,后来改成记忆化
F - B-number
好题啊,其实就是上面两题的组合
有个容斥的思想
可以被13整除的数且含13的数的数量等于 (所有的数的数量-不是13倍数的数)-是13倍数又不能含13的数
括号中的答案可以化简为n/13
剩下的就是数位Dp的模板了
Niro BC说记忆化垃圾,但是有些情况Dp真的麻烦,就两个条件就长成这鸟样%%
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int tmp[50],num[50],dp[50][2][2][13],ans,n; int solve(int n) { memset(dp,0,sizeof(dp)); int len=0; while(n) { tmp[++len]=n%10; n/=10; } int ans=0; for(int i=1;i<=len;i++) num[len-i+1]=tmp[i]; dp[0][1][0][0]=1; for(int i=0;i<=len;i++) for(int j=0;j<2;j++) for(int k=0;k<2;k++) for(int sta=0;sta<13;sta++) { int pp=dp[i][j][k][sta]; if(!pp) continue; if(i==len) if(sta==0) ans+=pp; if(!j) { if(!k) { for(int q=0;q<=9;q++) if(q!=1) dp[i+1][0][0][(sta*10+q)%13]+=pp;else dp[i+1][0][1][(sta*10+q)%13]+=pp; }else for(int q=0;q<=9;q++) if(q!=3) { if(q!=1) dp[i+1][0][0][(sta*10+q)%13]+=pp;else dp[i+1][0][1][(sta*10+q)%13]+=pp; } }else { if(!k) { for(int q=0;q<num[i+1];q++) if(q!=1) dp[i+1][0][0][(sta*10+q)%13]+=pp;else dp[i+1][0][1][(sta*10+q)%13]+=pp; if(num[i+1]!=1) dp[i+1][1][0][(sta*10+num[i+1])%13]+=pp;else dp[i+1][1][1][(sta*10+num[i+1])%13]+=pp; }else { for(int q=0;q<num[i+1];q++) if(q!=3) { if(q!=1) dp[i+1][0][0][(sta*10+q)%13]+=pp;else dp[i+1][0][1][(sta*10+q)%13]+=pp; } if(num[i+1]!=3) { if(num[i+1]!=1) dp[i+1][1][0][(sta*10+num[i+1])%13]+=pp;else dp[i+1][1][1][(sta*10+num[i+1])%13]+=pp; } } } } return ans; } int main() { while(~scanf("%d",&n)) { printf("%d\n",n/13-solve(n)+1); } }