HDU 2089 不要62

DESC: 问l, r范围内的没有4和相邻62的数有多少个。

 #include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std; int dp[][]; /*
3种状态:
0:前i位不含有不吉利数字 的数 的个数
1:前i位不含有不吉利数字且第i+1位数字是6 的数 的个数
2:前i位含有不吉利数字 的数 的个数
*/ //int dfs(int i, int s, bool e) {
// if (i==-1) return s==target_s;
// if (!e && ~dp[i][s]) return dp[i][s];
// int res = 0;
// int u = e?num[i]:9;
// for (int d = first?1:0; d <= u; ++d)
// res += dfs(i-1, new_s(s, d), e&&d==u);
// return e?res:f[i][s]=res;
//} int num[];
int len; int dfs(int pos, int s, bool e) {
if (pos == ) return s != ; //遍历完之后 判断当前数字是否是不吉利数
if (!e && dp[pos][s]) return dp[pos][s];
int sum = ;
int maxm = e?num[pos]:; for (int d = ; d <= maxm; ++d) {
if (s == ) {
if (d == ) sum += dfs(pos-, , e&&(maxm == d));
else if (d == ) sum += dfs(pos-, , e&&(maxm == d));
else sum += dfs(pos-, , e&&(maxm == d));
}
else if (s == ) {
if (d == || d == ) sum += dfs(pos-, , e&&(maxm == d));
else if (d == ) sum += dfs(pos-, , e&&(maxm == d));
else sum += dfs(pos-, , e&&(maxm == d));
}
else if (s == ) sum += dfs(pos-, , e&&(maxm == d));
}
return e?sum:dp[pos][s]=sum;
} int solve(int x) {
len = ;
while(x) {
num[len++] = x%;
x /= ;
}
len -= ;
return dfs(len, , );
} int main() {
int n, m;
// cout << solve(100) << endl;
memset(dp, , sizeof(dp));
while(~scanf("%d%d", &n, &m)) {
if (n == && m == ) break;
int l = solve(n-);
int r = solve(m);
int ans = r - l;
printf("%d\n", ans);
}
return ;
}

CF 55D Beautiful numbers 数位DP+离散化

DESC:如果一个数能整除它每一位上的数字,那么这个数叫做美丽数。问 l, r范围内的美丽数有多少个。

状态:dp[20][2600][55]; // dp[i][j][k] = x表示前i位mod2520的值为j 且最小公倍数是lcms[k] 的美丽数有多少个。

lcms[]是1~9的所有可能产生的最小公倍数的映射数组。

因为1~9能产生的最大公倍数是2520,又x%y==0 等价于x%y%z == 0在z是y的因子时成立。所以我们只需要维护前i个数mod2520的值,就可以知道前i个数mod1~9的值。

即第二维压缩到2520。

判断当前数字是否是beautiful number等价于判断当前数是否mod (前i位的最小公倍数==0)。

所以我们需要维护前i位的最小公倍数。

而1~9能产生的最小公倍数位最大值位2520,小于55个。所以可以离散化,即第三维压缩到55。

开始设计的状态是:dp[20][2]表示前i位是或不是beautiful number。中间用一个num数组标记当前值mod(1~9)的值,最后判断是否是beautiful number。

但是这样前i位的值就受后面的值的影响,不能记忆化了。遂超时。

 #include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#define LL long long
using namespace std; const int maxn = ; LL dp[][][]; // dp[i][j][k] = x表示前i位mod2520的值为j 且最小公倍数是lcms[k] 的美丽数有多少个。
int num[];
int lcms[]; int gcd(int a, int b) {
return (b>?gcd(b, a%b):a);
} int ggcd(int a, int b) {
if (a == ) return b;
else if (b == ) return a;
return (a*b)/gcd(a, b);
} void getLcms() { //初始化lcms[]
memset(lcms, -, sizeof(lcms));
int num = ;
for (int i=; i*i<=maxn; ++i) {
if (maxn%i== && lcms[i] == -) { //能整除i
lcms[i] = num++;
int temp = maxn / i;
if (lcms[temp] == -)
lcms[temp] = num++;
}
}
} LL dfs(int pos, int pre, int lcm, bool lim) { // 当前位 前面i位mod2520的值 前面i-1位的lcm 当前位是否到最后一个数
if (pos == -) return (pre%lcm==); // 搜索到最后一位
if (!lim && dp[pos][pre][lcms[lcm]] != -) return dp[pos][pre][lcms[lcm]]; // 记忆化搜索
int mnum = (lim==?:num[pos]);
LL sum = ;
for (int i=; i<=mnum; ++i) {
int npre = (pre * + i) % maxn;
int nlcm = ggcd(lcm, i);
sum += dfs(pos-, npre, nlcm, lim&&(i==mnum));
}
return (lim==?dp[pos][pre][lcms[lcm]]=sum:sum);
} LL solve(LL x) {
int len = ;
while(x) {
num[len++] = x % ;
x /= ;
}
return dfs(len-, , , ); //
} int main() {
// freopen("in.cpp", "r", stdin);
memset(dp, -, sizeof(dp));
getLcms();
int t;
scanf("%d", &t);
while(t--) {
LL l, r;
scanf("%I64d%I64d", &l, &r);
LL ans = solve(r) - solve(l-);
printf("%I64d\n", ans);
}
return ;
}

附上大腿的代码,虽然加了两个z和zz数组这两个智障产物,有碍观瞻。但是,被有爱的注释感动了。有木有。

 #include <iostream>
#include <cstring>
#include <cstdio>
#include <map>
#define LL long long
using namespace std; /*首先,个位数最大的公倍数是5*7*8*9=2520 ,所以,我们第二维通过取余可以压缩到2520;打表可知,0-9组成的不同的公倍数不超过55个,所以第三压缩至55*/
long long state[][][];/*state[a][b][c] a:第几位 b:前a位数字代表的数对2520取余的值 c:前a位数字的公倍数,在id数组中的映射的值 */
int num[];/*都懂*/
int id[];/*所有可能出现的公倍数的映射,比如,9是可能出现的公倍数,就让id[9]=1;依次类推*/
int z[][];/*z[a][b] 存储的是 a与b的公倍数的值,在程序中,a指第i位的取值,b指前i-1位的公倍数,即dfs中传入的变量lcm*/
int zz[][];/*zz[a][b] 存储的是 前i-1位为b 新一位为 a 得到的新的数为多少,这个和上面那个其实没什么用,只是当时没找到正确超时的点,所以加上的*/
int _index=,num2=;/*num2为上面id那个数组的计数*/ long long gcd(long long a,long long b)
{
return (b>?gcd(b,a%b):a);
}/*求公倍数*/ void init()/*初始化*/
{
for(int i=;i*i<=;i++)
{
if(%i==)
{
id[i]=num2++;
if(i*i!=)id[/i]=num2++;
}
}/*初始化id,原理是,2520必定是其他可能公倍数的倍数,因为2520=9*8*7*5,1,2,3,4,6,都是5 7 8 9的因数*/
for(int i=;i<=;i++)
{
for(int j=;j<=;j++)
{
z[i][j]=(i==?j:j*i/gcd(j,i));
}
for(int j=;j<=;j++)
{
zz[i][j]=(j*+i)%;
}
}/*z zz数组初始化*/
// cout<<1<<endl;
} void getnum(long long x)
{
memset(num,,sizeof(num));
_index=;
while(x>)
{
num[_index++]=x%;
x=x/;
}
// cout<<num[0]<<endl;
} long long dfs(int i,long long st,long long lcm,int limit)/*st 前i位代表的数 lcm 前i位数的公倍数*/
{
if(i==-)return st%lcm==;
if(!limit&&state[i][st][id[lcm]]!=-)return state[i][st][id[lcm]];
int maxn=limit?num[i]:;
//cout<<lcm<<endl;
long long ans=;
for(int j=;j<=maxn;j++)
{
ans+=dfs(i-,i?zz[j][st]:st*+j,z[j][lcm],limit&&j==maxn);
}
return limit?ans:state[i][st][id[lcm]]=ans;
} long long solve(long long x)
{
getnum(x);
return dfs(_index-,,,);
} int main()
{
freopen("codeforces55D.in.cpp","r",stdin);
int t;
long long l,r;
init();
memset(state,-,sizeof(state));/*这是当时超时的点,第一次写数位dp,不知道只需初始化一次,然后放到了solve里,就。。。翻车了*/
while(~scanf("%d",&t))
{
while(t--)
{
scanf("%I64d%I64d",&l,&r);
printf("%I64d\n",solve(r)-solve(l-));
}
}
return ;
}

HDU 4352 XHXJ's LIS

DESC:把一个数看做字符串,那么它的最长上升子序列的长度就是这个数的power value。

问,在l 到r的区间内,power value等于k的数有多少个。

(0<L<=R<2^63-1 and 1<=K<=10)

思路:

开始我是这样的:

2^63大概是10^18,k<=10,dp[20][1200][10] //1024

dp[i][j][k]=num 表示前i位的最长递增子序列为j长度为k的数的个数为num个。

dfs(pos, stu, num, lim)时。对当前位pos尝试放1~mnum的数字d时,

比较stu和nstu = stu | (1<<d)的大小..如果nstu>stu,dfs(pos-1, nstu, num+1, newlim);

否则的话。说明新加的数使得当前序列分成了两个。进行2次dfs。

1:原来的dfs(pos-1, stu, num, newlim).

2:包括新的数的LIS序列:dfs(pos-1, newstu, newnum, newlim);

此时的newstu:对原来的stu进行一次num[pos]+1~9的与运算,和num[pos]或运算。同时计算出newnum.

bug1:1253这样搜索到的结果是2,125 和 123,所以单纯的这样记录状态也是不行的。所以第二维的状态改为:100101表示长度为x的LIS最后一位是y,

这样的话,125遇见3就会变成123了。

bug2:本来觉得len记录下当前的LIS长度会省一些重复计算,然而,状态和len相同时,k不同时返回的结果也应该是不同的,所以这个记忆化是错的。

于是就变成了这样:

int dp[20][1200][20]; //dp[i][j][k] = x 表示前i位 LIS状态为j 长度为k的 数字个数为 x

对当前的d,检查stu如果后面没有大于d的数,就直接newstu= stu | (1<<d)。否则,找出第一个大于d的数,删掉,然后或操作(1<<d),得到newstu。

前导0:检查当前d是前导0的时候,stu不变,继续搜索。

如果当前的数字出现过了,stu也不变,继续搜索。

【因为这次大腿也间歇性老年痴呆,两个人一起卡了两天晚上的几个点。最后一个bug:dp不是long long大腿找了一个点... ...感觉这个题A的也很感人,

青岛赛站结束是不是就不会这样写题了呢。有点舍不得。】

 //HDU 4352

 #include <stdio.h>
#include <string.h>
#include <iostream>
#define LL long long
using namespace std; int num[];
LL dp[][][]; //dp[i][j][k] = x 表示前i位 LIS状态为j 长度为k的 数字个数为 x LL dfs(int pos, int stu, int k, int lim) { //lim==1 表示当前位有限制
if (pos == -) {
int cnt = ;
for (int i=; i<=; ++i) {
if (stu & (<<i)) {
cnt++;
}
}
return (cnt == k);
}
if (!lim && dp[pos][stu][k] != -) return dp[pos][stu][k];
int mnum = (lim == ? : num[pos]);
LL sum = ;
for (int d=; d<=mnum; ++d) {
bool aft = false;
int tstu = stu;
bool pre = true;
if (d == ) {
for (int i=; i<=; ++i) {
if (tstu & (<<i)) pre = false;
}
if (pre) {
sum += dfs(pos-, stu, k, lim&&(d==mnum));
continue;
}
}
if (tstu & (<<d)) {
sum += dfs(pos-, stu, k, lim&&(d==mnum));
continue;
}
for (int i=d+; i<=; ++i) {
if (tstu & (<<i)) {
tstu = (stu ^ (<<i));
tstu |= (<<d);
aft = true;
break;
}
}
if (aft==false) { // 后面没有比他大的 可以直接加上
tstu = (stu | (<<d));
sum += dfs(pos-, tstu, k, lim&&(d==mnum));
}else sum += dfs(pos-, tstu, k, lim&&(d==mnum));
}
return (lim == ? dp[pos][stu][k] = sum : sum);
} LL solve(LL x, int k) {
if (x == ) return ;
int len = ;
while(x) {
num[len++] = x % ;
x /= ;
}
return dfs(len-, , k, );
} int main() {
// freopen("in.cpp", "r", stdin);
memset(dp, -, sizeof(dp));
int t;
scanf("%d", &t);
int cas = ;
while(t--) {
LL l, r;
int k;
scanf("%lld%lld%d", &l, &r, &k);
LL ans = solve(r, k) - solve(l-, k);
printf("Case #%d: %lld\n", cas++, ans);
}
return ;
}

数位DP入门的更多相关文章

  1. xbz分组题B 吉利数字 数位dp入门

    B吉利数字时限:1s [题目描述]算卦大湿biboyouyun最近得出一个神奇的结论,如果一个数字,它的各个数位相加能够被10整除,则称它为吉利数.现在叫你计算某个区间内有多少个吉利数字. [输入]第 ...

  2. 数位dp入门 hdu2089 不要62

    数位dp入门 hdu2089 不要62 题意: 给定一个区间[n,m] (0< n ≤ m<1000000),找出不含4和'62'的数的个数 (ps:开始以为直接暴力可以..貌似可以,但是 ...

  3. hdu3555 Bomb 数位DP入门

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3555 简单的数位DP入门题目 思路和hdu2089基本一样 直接贴代码了,代码里有详细的注释 代码: ...

  4. HDU 2089 不要62【数位DP入门题】

    不要62 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  5. HDU 2089 不要62(数位dp入门)

    题意:统计区间 [a,b] 中不含 4 和 62 的数字有多少个. 题解:这是数位DP的入门题了,首先要理解数DP的原理,DP[i][j]:代表第i位的第j值,举个栗子:如4715   数位数是从右向 ...

  6. HDU 2089 - 不要62 - [数位DP][入门题]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2089 Time Limit: 1000/1000 MS (Java/Others) Memory Li ...

  7. LightOJ 1140 计数/数位DP 入门

    题意: 给出a,b求区间a,b内写下过多少个零 题解:计数问题一般都会牵扯到数位DP,DP我写的少,这道当作入门了,DFS写法有固定的模板可套用 dp[p][count] 代表在p位 且前面出现过co ...

  8. HDU-2089不要62-暴力或数位DP入门

    不要62 题意:给定区间,求在这个区间中有多少个数字,不包含4且不包含62: 这道题作为数位DP的入门题: 暴力也是可以过 #include<cstdio> #include <io ...

  9. 数位dp入门 HDU 2089 HDU 3555

    最基本的一类数位dp题,题目大意一般是在a~b的范围,满足某些要求的数字有多少个,而这些要求一般都是要包含或者不包含某些数字,或者一些带着数字性质的要求,一般来说暴力是可以解决这一类问题,可是当范围非 ...

随机推荐

  1. java反射类内容获取

    private void DtoReflect(Object obj, MqDto mqDto) throws Exception { Map map = getMap(mqDto); if(obj= ...

  2. Maven常用的命令

    mvn archetype:generate 产生一个新的项目 mvn compile 会执行 mvn resources:resources    mvn compiler:compile两个过程 ...

  3. Iptables工作原理使用详解

    Iptables防火墙简介 Iptables名词和术语 Iptables工作流程 基本语法 Filter 参数说明 NAT表: Icmp协议 TCP FLAG 标记 什么是状态检测 iptables的 ...

  4. Android 网络开发之WIFI

    WIFI就是一种无线联网技术,常见的是使用无线路由器.那么在这个无线路由器的信号覆盖的范围内都可以采用WIFI连接的方式进行联网.如果无线路由器连接了一个ADSL线路或其他的联网线路,则又被称为&qu ...

  5. 【转】Checkpoint--与lazy writer区别

      checkpoint目的是减少数据库的恢复时间(服务奔溃或重启服务后的恢复),而lazy writer的目的是保证SQL OS 有空闲缓存块和系统有一定可用内存. Checkpoint和lazyW ...

  6. Python3基础 访问列表指定索引值的元素

    镇场诗:---大梦谁觉,水月中建博客.百千磨难,才知世事无常.---今持佛语,技术无量愿学.愿尽所学,铸一良心博客.------------------------------------------ ...

  7. Android 基础概念了解

    Android 的前世今生Android 系统框架Android 主要组成 部分Android 常用的操作 Android 的前世今生 Android 的诞生 2003年10月,有"Andr ...

  8. devenv.exe assert failure

    使用vs2010,创建mvc2 web application项目,编译之后,调试报错 https://www.asp.net/mvc/overview/older-versions-1/gettin ...

  9. Java开发中经典的小实例-(冒泡法)

    public class Test25 {    public static void main(String[] args) {        // 冒泡法        int[] array = ...

  10. jquery总结06-动画事件01-基础显示和隐藏

    动画事件 .hide(option) 动画隐藏  会保存元素的原始属性值 $("#a2").hide({ duration: 3000, complete: function() ...