数位DP问题整理(一)
第一题:Amount of degrees (ural 1057)
题目链接:http://acm.timus.ru/problem.aspx?space=1&num=1057
题意:[x,y]范围内的数,可以拆分成k个b进制的不同幂的和 的数字有多少。
我们可以将x转换成二进制来讨论。二进制转化时,找到第一个非0非1的数,将其及其后面的数都变为1.
那么问题就变成了求[0,x]范围内,二进制表示中含有k个1的数字有多少个。
求[x,y]区间相减。我们可以给数建立0,1的表示树。
在求高度为i的完全二叉树中含有j个1的路径有多少个时,递推式为:f[i,j] = f[i-1,j-1] + f[i-1,j]
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <map>
#include <queue>
#include <algorithm>
using namespace std; int f[35][35];
int d[35];
//高度为i(i>=0)时,含有j个1的个数
void init()
{
memset(f,0,sizeof(f));
f[0][0] = 1;
for(int i=1;i<=31;i++)
{
f[i][0] = 1;
for(int j=1;j<=i;j++)
{
f[i][j] = f[i-1][j-1] + f[i-1][j];
}
}
}
//[0,x]范围内二进制含有k个1的个数
int calc(int x,int k)
{
//路径上含有的1的个数
int tot = 0;
int ans = 0;
for(int i=31;i>0;i--)
{
if(x&(1<<i))
{
tot++;
if(tot>k) break;
x ^= (1<<i);
}
if((1<<(i-1))<=x) ans += f[i-1][k-tot];
}
if(tot + x == k) ans++;
return ans;
}
//b进制转化为二进制
int transfer(int b,int x)
{
int m = 0;
int ans = 0;
while(x)
{
d[m++] = x % b;
x/=b;
}
for(int i=m-1;i>=0;i--)
{
if(d[i]>1)
{
for(int j=i;j>=0;j--) ans |= (1<<j);
}
else ans |= d[i]<<i;
}
return ans;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int x,y;
int k,b;
init();
while(scanf(" %d %d",&x,&y)!=EOF)
{
scanf(" %d %d",&k,&b);
x = transfer(b,x-1);
y = transfer(b,y);
printf("%d\n",calc(y,k) - calc(x,k));
}
return 0;
}
第二题:windy数。
题意:求给定区间范围内的,求相邻数位之差绝对值不小于2的数的个数。
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1026
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <map>
#include <queue>
#include <algorithm>
using namespace std; int A[12]; int f[12][10]; //f[i][j]代表长度为i,最高位为j的windy数个数
void init()
{
memset(f,0,sizeof(f));
for(int i=0;i<10;i++) f[1][i] = 1;
for(int i=2;i<=10;i++)
{
for(int j=0;j<10;j++)
{
for(int k=0;k<10;k++)
{
if(abs(j-k)>1) f[i][j] += f[i-1][k];
}
}
}
}
//(0,a)范围内的windy数个数
int calc(int a)
{
int m = 0;
while(a)
{
A[m++] = a%10;
a/=10;
}
int ans = 0;
//先处理长度小于m的windy数的个数
for(int i=1;i<m;i++)
{
//题目要求不含前导0
for(int j=1;j<10;j++)
{
ans += f[i][j];
}
}
//长度等于m且最高位和原数不同且小于原数的windy数
for(int j=1;j<A[m-1];j++) ans += f[m][j];
//依次循环将最高位 变为和原数相同
for(int i=m-1;i>=1;i--)
{
for(int j=0;j<A[i-1];j++)
{
if(abs(j-A[i]) > 1) ans += f[i][j];
}
if(abs(A[i] - A[i-1])<=1) break;
}
return ans;
} int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int a,b;
init();
while(scanf(" %d %d",&a,&b)!=EOF)
{
int ans = calc(b+1) - calc(a);
printf("%d\n",ans );
}
return 0;
}
第三题:Hdu 2089 不要62
题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=2089
求给定区间中不含有62和4的数的个数。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <map>
#include <queue>
#include <algorithm>
using namespace std; int dp[10][3]; int A[10]; //(0,a]范围内有多少个吉利数
int calc(int a)
{
int sum = a;
int m = 0;
int ans = 0;
bool flag = false;
while(a)
{
A[++m] = a%10;
a/=10;
}
A[m+1] = 0;
for(int i=m;i>=1;i--)
{
ans += dp[i-1][2] * A[i];
if(flag)
{
ans += dp[i-1][0] * A[i];
}
else
{
if(A[i]>4) ans += dp[i-1][0];
if(A[i+1] == 6 && A[i]>2) ans += dp[i][1];
if(A[i]>6) ans += dp[i-1][1];
if(A[i] == 4 || (A[i+1] == 6 && A[i] == 2)) flag = true;
}
}
//数本身
if(flag) ans++;
return sum - ans;
} //dp[i][0]:长度<=i的吉利数个数
//dp[i][1]:长度为i,且最高位含有2的吉利数个数
//dp[i][2]:长度<=i的非吉利数个数
void init()
{
memset(dp,0,sizeof(dp));
dp[0][0] = 1;
for(int i=1;i<=8;i++)
{
dp[i][0] = dp[i-1][0]*9 - dp[i-1][1];
dp[i][1] = dp[i-1][0];
dp[i][2] = dp[i-1][0] + dp[i-1][1] + dp[i-1][2] * 10;
}
} int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int a,b;
init();
while(scanf(" %d %d",&a,&b)!=EOF)
{
if(a == 0 && b == 0) break;
int ans = calc(b) - calc(a-1);
printf("%d\n",ans);
}
return 0;
}
第四题:Hdu 3555 Bomb
题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=3555
题意:求给定区间的含有49的数的个数。
方法与上题类似,比上题要简单许多。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <map>
#include <queue>
#include <algorithm>
using namespace std; #define LL __int64
LL dp[25][3]; int A[25]; //(0,a]范围内有多少个吉利数
LL calc(LL a)
{
int m = 0;
LL ans = 0;
bool flag = false;
while(a)
{
A[++m] = a%10;
a/=10;
}
A[m+1] = 0;
for(int i=m;i>=1;i--)
{
ans += dp[i-1][2] * A[i];
if(flag)
{
ans += dp[i-1][0] * A[i];
}
else
{
if(A[i]>4) ans += dp[i-1][1];
if(A[i+1] == 4 && A[i] == 9) flag = true;
}
}
//数本身
if(flag) ans++;
return ans;
} //dp[i][0]:长度<=i的不含49的数的个数
//dp[i][1]:长度为i,且最高位含有9的不含49的数的个数
//dp[i][2]:长度<=i的含有49的数个数
void init()
{
memset(dp,0,sizeof(dp));
dp[0][0] = 1;
for(int i=1;i<=22;i++)
{
dp[i][0] = dp[i-1][0]*10 - dp[i-1][1];
dp[i][1] = dp[i-1][0];
dp[i][2] = dp[i-1][2] * 10 + dp[i-1][1];
}
} int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int t;
LL a;
init();
scanf(" %d",&t);
while(t--)
{
scanf(" %I64d",&a);
LL ans = calc(a);
printf("%I64d\n", ans);
}
return 0;
}
第五题:Hdu 3709 Balanced Number
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3709
平衡数。枚举平衡位置。采用记忆化搜索的方式记录已有的值。加适当剪枝。然后排除掉重复的0即可。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <map>
#include <queue>
#include <algorithm>
using namespace std; #define LL long long
#define Maxn 20 LL dp[Maxn][Maxn][2005];
int digit[Maxn]; LL dfs(int pos,int pivot,int pre,bool limit)
{
if(pos<=0) return pre == 0;
if(pre<0) return 0;
if(!limit && dp[pos][pivot][pre]!=-1) return dp[pos][pivot][pre];
int end = limit ? digit[pos] : 9;
LL ans = 0;
for(int i=0;i<=end;i++)
{
ans += dfs(pos-1,pivot,pre + i*(pos-pivot),limit && (i == end));
}
if(!limit) dp[pos][pivot][pre] = ans;
return ans;
} LL calc(LL a)
{
if(a<0) return 0;
int len = 0;
LL ans = 0;
while(a>0)
{
digit[++len] = a%10;
a/=10;
}
for(int i=1;i<=len;i++)
{
ans += dfs(len,i,0,1);
}
ans = ans - len + 1;
return ans;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int t;
LL x,y;
scanf(" %d",&t);
memset(dp,-1,sizeof(dp));
while(t--)
{ scanf(" %I64d %I64d",&x,&y);
printf("%I64d\n",calc(y) - calc(x-1) );
}
return 0;
}
第六题:Hoj 1983 Beautiful numbers
题目链接:http://acm.hit.edu.cn/hoj/problem/view?id=1983
题意:如果一个数能够被其每个数位的数都整除,那么这个数就叫做美丽数。
基本思路是用:dp[len][mod][lcm]表示<=len的长度中,此数为mod,各数位的最小公倍数为lcm的数的个数来进行记忆化搜索。方法和上一题类似。
但我们发现,len在[1,20]范围内,mod在[1,1^18]范围内,lcm在[1,2520]范围内。所以dp数组肯定超内存。
下面我们来进行内存优化:
假设这个数为a,各个数位的值分别为ai,那么我们发现lcm(ai) | a.
而[1,9]的最小公倍数是2520.那么lcm(ai) | 2520, 所以lcm(ai) | (a%2520).
所以第二维大小我们可以从1^18降到2520,方法是%2520.
现在的dp数组的内存是20*2520*2520,还是很大。
然后我们再考虑:
我们发现某一个数的各个数位的数的最小公倍数最大是2520,而且只能是2520的公约数。而2520的公约数有48个。所以第三维我们只用[50]的空间就行了。
方法是用Hash进行离散化。‘
这样内存就成了20*2520*50,可以拿下这道题目了。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <map>
#include <queue>
#include <algorithm>
using namespace std; #define LL long long LL dp[20][2525][55];
int digit[20];
int hash[2525]; int gcd(int a,int b)
{
if(b == 0) return a;
return gcd(b,a%b);
}
int calc_lcm(int a,int b)
{
return a/gcd(a,b)*b;
}
LL dfs(int pos,int mod,int lcm,bool limit)
{
LL ans = 0;
if(pos<=0) return mod % lcm == 0;
if(!limit && dp[pos][mod][hash[lcm]]!=-1) return dp[pos][mod][hash[lcm]];
int end = limit ? digit[pos] : 9;
for(int i=0;i<=end;i++)
{
ans += dfs(pos-1,(mod*10+i)%2520,i?calc_lcm(lcm,i):lcm,limit && (i==end));
}
if(!limit) dp[pos][mod][hash[lcm]] = ans;
return ans;
} LL calc(LL a)
{
if(a<0) return 0;
int len = 0;
while(a>0)
{
digit[++len] = a%10;
a/=10;
}
//0也当作其中的一个美丽数,因为两者相减会抵消掉
LL ans = dfs(len,0,1,1);
return ans;
}
void init()
{
memset(dp,-1,sizeof(dp));
int id = 0;
for(int i=1;i*i<=2520;i++)
{
if(2520%i == 0)
{
hash[i] = id++;
if(i*i!=2520) hash[2520/i] = id++;
}
}
//printf("id = %d\n", id);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
init();
int t;
LL x,y;
while(scanf(" %lld %lld",&x,&y)!=EOF)
{
printf("%lld\n",calc(y) - calc(x-1));
}
return 0;
}
第七题:吉哥系列故事——恨7不成妻
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4507
与上一题做法也类似,只不过dp需要保存三种值,所以把它结构体了
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <map>
#include <queue>
#include <algorithm>
using namespace std; #define MOD 1000000007
#define LL __int64 int digit[20];
LL power[20]; struct Node
{
LL n,s,sq;
}dp[20][10][10]; Node dfs(int pos,int mod,int modSum,bool limit)
{
if(pos<=0)
{
Node t;
t.n = (mod!=0 && modSum!=0);
t.s = t.sq = 0;
return t;
}
if(!limit && dp[pos][mod][modSum].n!=-1) return dp[pos][mod][modSum];
int end = limit ? digit[pos] : 9;
Node ans,temp;
ans.n = ans.s = ans.sq = 0;
for(int i=0;i<=end;i++)
{
if(i == 7) continue;
temp = dfs(pos-1,(mod*10+i)%7,(modSum+i)%7,limit && (i == end));
ans.n = (ans.n + temp.n)%MOD;
ans.s = (ans.s + temp.s + ((i * power[pos])%MOD * temp.n) % MOD) % MOD ;
ans.sq = (ans.sq + temp.sq + ((2*i*power[pos])%MOD*temp.s)%MOD + (((i*i*power[pos])%MOD*power[pos])%MOD*temp.n)%MOD)%MOD;
}
if(!limit) dp[pos][mod][modSum] = ans;
return ans;
}
LL calc(LL a)
{
int len = 0;
while(a>0)
{
digit[++len] = a%10;
a/=10;
}
Node ans = dfs(len,0,0,true);
return ans.sq;
}
void init()
{
memset(dp,-1,sizeof(dp));
memset(power,0,sizeof(power));
power[1] = 1;
for(int i=2;i<=19;i++)
{
power[i] = (power[i-1] * 10)%MOD;
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int t;
LL l,r;
init();
scanf(" %d",&t);
while(t--)
{
scanf(" %I64d %I64d",&l,&r);
LL ans = (calc(r) - calc(l-1) + MOD)%MOD;
printf("%lld\n", ans);
}
return 0;
}
数位DP问题整理(一)的更多相关文章
- 数位dp知识点整理
题解报告:hdu 2089 不要62 Problem Description 杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer).杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌 ...
- 数位dp整理
数位dp的思想就在于递归,记录当前的某一个唯一状态,依次递归下去,要注意唯一. 数位dp常设的状态有当前位置,上一数字,是否具有前导零,是否有限制. 1.CodeForces 55DBeautiful ...
- 数位dp整理 && 例题HDU - 2089 不要62 && 例题 HDU - 3555 Bomb
数位dp: 数位dp是一种计数用的dp,一般就是要统计一个区间[li,ri]内满足一些条件数的个数.所谓数位dp,字面意思就是在数位上进行dp.数位的含义:一个数有个位.十位.百位.千位......数 ...
- Kuangbin 带你飞 数位DP题解
以前一直不知道该咋搞这个比较好. 感觉推起来那个数字好麻烦.后来有一种比较好的写法就是直接的DFS写法.相应的ismax表示当前位是否有限制. 数位DP也是有一种类似模版的东西,不过需要好好理解.与其 ...
- HYSBZ 1026: windy数(数位DP)
类型:数位DP题意:不含前导零且相邻两个数字之差至少为2的正整数被称为windy数.问[A,B]之间windy数的个数.(1 <= A <= B <= 2000000000 ) 思路 ...
- BZOJ 3329: Xorequ(数位dp+递推)
传送门 解题思路 可以把原式移项得\(x\)^\(2x\)=\(3x\),而\(x+2x=3x\),说明\(x\)二进制下不能有两个连续的\(1\).那么第一问就是一个简单的数位\(dp\),第二问考 ...
- 【BZOJ1662】[Usaco2006 Nov]Round Numbers 圆环数 数位DP
[BZOJ1662][Usaco2006 Nov]Round Numbers 圆环数 Description 正如你所知,奶牛们没有手指以至于不能玩"石头剪刀布"来任意地决定例如谁 ...
- bzoj1026数位dp
基础的数位dp 但是ce了一发,(abs难道不是cmath里的吗?改成bits/stdc++.h就过了) #include <bits/stdc++.h> using namespace ...
- uva12063数位dp
辣鸡军训毁我青春!!! 因为在军训,导致很长时间都只能看书yy题目,而不能溜到机房鏼题 于是在猫大的帮助下我发现这道习题是数位dp 然后想起之前讲dp的时候一直在补作业所以没怎么写,然后就试了试 果然 ...
随机推荐
- 转载:Source Insight查看ARM汇编源程序 && 高亮显示程序 && Source Insight打开project窗口出错
(1)Source Insight查看ARM汇编源程序.做ARM嵌入式开发时,有时得整汇编代码,但在SIS里建立PROJECT并ADD TREE的时候,根据默认设置并不会把该TREE里面所有汇编文件都 ...
- HDU 4493 Tutor 水题的收获。。
题目: http://acm.hdu.edu.cn/showproblem.php?pid=4493 题意我都不好意思说,就是求12个数的平均数... 但是之所以发博客,显然有值得发的... 这个题最 ...
- Codeforces Round #321 div2
好像前几场的题解忘记写了, Orz 状态太差, 平均出两题 都不好意思写了 , 连掉4场, 都要哭晕了. 很水的一场, 写完A B C就去睡了 D题其实不难, E题研究Ing(已用一种奇怪的姿势 ...
- 教你在你的应用程序中扩展使用dynamic类型
教你在你的应用程序中扩展使用dynamic类型 相信大家在开发中经常会接触到mvc中的ViewBag,有心的同学会,发现这就是一个dynamic类型,查看源码一谈究竟,本文也是根据dynamic来扩展 ...
- csu 10月 月赛 I 题 The Contest
Description 殷犇有很多队员.他们都认为自己是最强的,于是,一场比赛开始了~ 于是安叔主办了一场比赛,比赛有n个题目,每个题目都有一个价值Pi和相对能力消耗Wi,但是有些题目因为太坑不能同时 ...
- the apply of backbone
http://www.developer.com/print/lang/jscript/creating-a-javascript-driven-online-notebook-with-backbo ...
- 【整理】各种Python的IDE(集成开发环境)的总结和对比
原地址:http://www.tuicool.com/articles/rMVJNn 原文 http://www.crifan.com/summary_common_python_ide_pyscr ...
- 【HDOJ】1512 Monkey King
左偏树+并查集.左偏树就是可合并二叉堆. /* 1512 */ #include <iostream> #include <string> #include <map&g ...
- 【SSSP】A forward-backward single-source paths algorithm
0. 引子基础的算法和数据结构已经学习的差不多了,上学期期末就打算重点研究研究STOC和FOCS上面的论文.做这件事情的初衷是了解别人是如何改进原有算法的,搞清楚目前比较热的算法问题有哪些,更重要的是 ...
- Super Phyllis(穷举+搜索)
http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2723 题意:给出一些字符串u,v,代表u-&g ...