hdu3555+cf55D 数位dp入门题
来源:互联网 发布:淘宝店铺名化妆品 编辑:程序博客网 时间:2024/05/01 22:12
就做了3道入门题。。。的瞎总结,一般题目为计算l~r中间有多少个符合条件的数,l和r的范围就是longlong的范围吧=,=
最后结果为cal(r)- cal(l-1)
dp[i]表示到第几位为止,例如dp[4]表示0000~9999
dp[i][j][k] j,k...表示状态位,就表示 i 个数字位,状态为jk的数有多少个
此题dp[i][0]表示之前不含49且前位i-1不是4,dp[maxn][1]表示不含49前位是4,dp[maxn][2]表示含49
cal函数把l或r拆成一个一个的数字,然后dfs从高位开始往低位枚举,枚举每位上是0~9(根据limit,不一定0~9都可以)的数字,都有多少个符合的数。
limit 0/1 表示后面数字是否有上限,例如。。r=5824,现在第一位已为5,第二位枚举为8的时候limit就变为1,即选第三位的时候有上限,上限是第三位的数字2,如果limit为0,就这位没有限制最高位9而不是当前pos的数字,这样保证转移到数比n小。
#include <cstdio>#include <cstring>#include <iostream>using namespace std;#define ll long long#define FOR(i,j,k) for(int i =j; i<=k ;i++)const int maxn =50;ll dp[maxn][5];//flag 0 表示不含49前位不是4,1表示不含49前位是4,2表示含49int digit[maxn];ll dfs(int pos,int flag,int limit){ if(pos == 0) return flag == 2; if(!limit && dp[pos][flag] != -1) //如果没有限制,直接返回dp[pos][flag] return dp[pos][flag]; int ed = limit ? digit[pos] :9; ll ans =0; FOR(i, 0 ,ed) { int f2= flag; if(flag == 1 && i== 9) f2 = 2; if(flag == 0 && i == 4) f2 =1; if(flag == 1 && i!=4 && i!=9) f2= 0; ans += dfs(pos -1,f2,limit && i == ed);//如果i!=ed,下一位就没有限制 } if(!limit) dp[pos][flag] = ans; return ans;}ll cal(ll n){ memset(digit,0,sizeof(digit)); int len = 0; while(n) { len++; digit[len] = n%10; n /=10; } return dfs(len ,0 ,1 );}int main(){ int T; cin>>T; memset(dp, -1 ,sizeof(dp)); ll n; while(T--) { scanf("%I64d", &n); printf("%I64d\n", cal(n));; } return 0;}
cf55D题那个状态真是!!炒鸡!!炒鸡!!棒!!!特别!!特别!!厉害!!
如果一个数能被自己所有的除零外的数字整除,就beautiful,就算多少个beautiful数
数字只有0~9 ,lcm(1,2,3,4。。。9) = 2520
如果一个数能整除2520就一定beautiful,所以presum计算枚举完的前面的数(这里的数是前几位十进制的数)先模2520
prelcm计算枚举到当前前面几个数字的lcm
当全部枚举完的时候,presum % prelcm == 0 说明这个数字beautiful
此题dp[i][j][k] 表示有i位数字,j=前面数%2520,k为前面数的最大公约数是2520的第几个约数(采用给2520的约数编号,缩小第三维的大小)
同样flag记录是不是有限制,没有限制的话直接返回dp中的数不重复计算。。
#include <cstdio>#include <cstring>#include <iostream>using namespace std;#define ll long long#define FOR(i,j,k) for(int i =j; i<=k ;i++)const int mod =2520;int indx[mod+10];ll dp[25][mod+10][50];int digit[25];void init(){ int num = 0; memset(indx,-1,sizeof(indx)); memset(dp,-1,sizeof(dp)); FOR(i,1,mod) { if(mod % i == 0) indx[i] = num,num++;//i 是第indx[i]个被2520的约数 }}ll gcd(ll a,ll b){ return b== 0 ?a :gcd(b,a%b);}ll lcm(ll a,ll b){ return a*b/gcd(a,b);}ll dfs(int pos,int presum, int prelcm ,bool flag){ if(pos == -1) return presum % prelcm == 0; if(!flag && dp[pos][presum][indx[prelcm]] != -1) return dp[pos][presum][indx[prelcm]];//persum 之前模完余几,indx[prelcm] 之前数的最大公倍数是2520的第几个约数 int ed = flag ?digit[pos] :9; ll ans=0; FOR(i,0,ed) { int nowlcm = prelcm; int nowsum = ( presum*10 +i) % mod; if(i) nowlcm = lcm (prelcm, i); ans += dfs (pos -1, nowsum , nowlcm, flag && i == ed); } if(!flag) dp[pos][presum][indx[prelcm]]= ans; return ans; }ll cal(ll n){ memset(digit,0,sizeof(digit)); int len = 0; while(n) { digit[len] = n%10; len++; n /=10; } return dfs(len-1 ,0 ,1,1 );}int main(){ int T; cin>>T; init(); ll l,r; while(T--) { cin>>l>>r; cout<<cal(r) - cal(l-1)<<endl; } return 0;}
- hdu3555+cf55D 数位dp入门题
- 数位DP HDU3555+CF55D+HDU2089
- hdu3555 数位dp入门题
- hdu3555 数位dp 入门
- HDU3555 数位DP入门!
- hdu3555(数位dp入门题)
- 【HDU3555】Bomb-数位DP入门题
- hdu3555 + 2089 (数位dp入门)
- HDU3555 常规数位dp入门....
- 【数位DP入门】HDU3555 Bomb
- hdu3555 Bomb ——数位DP入门题
- HDU3555:Bomb(数位dp入门)
- HDU3555 Bomb 数位DP经典题
- 【数位DP】 hdu3555 Bomb
- 【hdu3555】【数位DP】Bomb
- 【数位DP】Bomb HDU3555
- hdu3555 Bomb 数位DP
- hdu3555数位dp
- Visual Studio 2015配置opencv开发环境
- uri和url
- s2sh中hibernate中identity和increment区别
- 第二周笔记
- Unity3d AR 增强现实技术列表(2016年3月31日更新)
- hdu3555+cf55D 数位dp入门题
- 我的第一篇博客以及HDU 1003 Max Sum
- oracle基础数据库语句
- Array
- Linux学习之路——网络基础知识点汇总
- druid配置
- 常用工具的网址
- 重拾c语言——指针与数组
- java基础-001