数位DP总结

By Wine93 2013.7

1.学习链接

[数位DP] Step by Step  

http://blog.csdn.net/dslovemz/article/details/8540340

[总结] 数位统计模板

http://www.cnblogs.com/jffifa/archive/2012/08/17/2644847.html

2.各人总结领悟

数位DP关键在于状态的设计,设计状态时要考虑当前状态(如dp[pos][s1][s2]),一旦当前状态确定后,pos后面几位随便怎么填结果都一样.一旦达到这个,状态设计就正确了!

3.例题(已解决)

一. HDU 2089  不要62

http://acm.hdu.edu.cn/showproblem.php?pid=2089

题意:求区间[n,m]数字中不含62的数字个数 (0<n≤m<1000000)

分析:简单数位DP (暴力也可以解决)

# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std; # define N
int dig[N],dp[N][]; //statu 6:1 62:2 4:3
int dfs(int pos,int statu,int limit)
{
int i,s,end,sum=;
if(pos==-)
return statu==;
if(!limit&&dp[pos][statu]!=-)
return dp[pos][statu];
end=limit?dig[pos]:;
for(i=;i<=end;i++)
{
s=statu;
if(s==||s==&&i==||i==)
s=;
else if(i==)
s=;
else
s=;
sum+=dfs(pos-,s,limit&&i==end);
}
if(!limit)
dp[pos][statu]=sum;
return sum;
} int calc(int n)
{
int len=-;
memset(dp,-,sizeof(dp));
while(n)
{
dig[++len]=n%;
n/=;
}
return dfs(len,,);
} int main()
{
// freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
int n,m;
while(scanf("%d%d",&n,&m)!=EOF&&(n+m))
printf("%d\n",m-n+-(calc(m)-calc(n-)));
return ;
}

HDU 2089

二. HDU 3555 Bomb

http://acm.hdu.edu.cn/showproblem.php?pid=3555

题意:求[1,N]数字中不含49的数字个数N (1 <= N <= 2^63-1)

分析:简单数位DP

  # include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std; # define LL long long
# define N int dig[N];
LL dp[N][]; //statu 4:1 49:2
LL dfs(int pos,int statu,int limit)
{
int i,end,s;
LL sum=;
if(pos==-)
return statu==;
if(!limit&&dp[pos][statu]!=-)
return dp[pos][statu];
end=limit?dig[pos]:;
for(i=;i<=end;i++)
{
s=statu;
if(s==||s==&&i==)
s=;
else if(i==)
s=;
else
s=;
sum+=dfs(pos-,s,limit&&i==end);
}
if(!limit)
dp[pos][statu]=sum;
return sum;
} void calc(LL n)
{
int len=-;
memset(dp,-,sizeof(dp));
while(n)
{
dig[++len]=n%;
n/=;
}
LL ans=dfs(len,,);
printf("%I64d\n",ans);
} int main()
{
int t;
LL n;
// freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
scanf("%d",&t);
while(t--)
{
scanf("%I64d",&n);
calc(n);
}
return ;
  }

HDU 3555

三. UESTC 1307 windy数

http://www.acm.uestc.edu.cn/code.php?sid=442734

题意:求区间[A,B]之间的windy数  (windy数:不含前导0,并且相邻2个数字之间差至少为2)1 <= A <= B <= 2000000000

分析:状态设计时多加一个前导0,并且记录当前选择的数字(pre)递归到下一位数字选择时利于判断是否相差2

   #include<cstdio>
# include<cmath>
# include<cstring>
using namespace std; typedef long long ll;
# define N
ll dp[N][N][],a,b;
int dig[N]; ll dfs(int pos,int pre,int statu,int limit) //statu; 1:前面全部是0 2:满足条件 0:不满足条件
{
int i,end,s;
ll sum=;
if(pos==-)
return statu==;
if(!limit&&dp[pos][pre][statu]!=-)
return dp[pos][pre][statu];
end=limit?dig[pos]:;
for(i=;i<=end;i++)
{
if(statu==&&i!=)
s=;
else if(statu==&&i==)
s=;
else if(statu==&&abs((double)(pre-i))>=)
s=;
else
continue;
sum+=dfs(pos-,i,s,limit&&end==i);
}
if(!limit)
dp[pos][pre][statu]=sum;
return sum;
} ll calc(ll n)
{
int len=-;
memset(dp,-,sizeof(dp));
while(n)
{
dig[++len]=n%;
n/=;
}
return dfs(len,,,);
} int main()
{
while(scanf("%I64d%I64d",&a,&b)!=EOF)
printf("%lld\n",calc(b)-calc(a-));
return ;
}

UESTC 1307

四. HDU 3652 B-number

http://acm.hdu.edu.cn/showproblem.php?pid=3652

题意:求[1,n]数字中数字13并且能被13整除的数字个数(1 <= n <= 1000000000).

分析:多加一个状态,记录到当前位的数 最后判断下这个数能否被13整除

  # include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std; # define N
# define LL long long
LL dp[N][N][N];
int dig[N]; // statu 0:什么都没有 1:含有1 2:含有13
LL dfs(int pos,int pre,int statu,int limit)
{
int i,s,end;
LL res=;
if(pos==-)
return statu==&&pre==;
if(!limit&&dp[pos][pre][statu]!=-)
return dp[pos][pre][statu];
end=limit?dig[pos]:;
for(i=;i<=end;i++)
{
s=statu;
if(s==||(s==&&i==))
s=;
else if(i==)
s=;
else
s=;
res+=dfs(pos-,(pre*+i)%,s,limit&&i==end);
}
if(!limit)
dp[pos][pre][statu]=res;
return res;
} LL calc(LL n)
{
int len=-;
memset(dp,-,sizeof(dp));
while(n)
{
dig[++len]=n%;
n/=;
}
return dfs(len,,,);
} int main()
{
LL n;
while(scanf("%I64d",&n)!=EOF)
printf("%I64d\n",calc(n));
return ;
  }

HDU 3652

五. HDU 3709 Balanced Number

http://acm.hdu.edu.cn/showproblem.php?pid=3709

题意:求一个区间[x,y]内是平衡数的数个数 (平衡数:如果一个数能找到一个平衡点,使这个平衡点2边的每个数字乘以这个数字到平衡点距离的积的和相等  如 4139就是个平衡数 因为4*2 + 1*1 = 9*1)   (0 ≤ x ≤ y ≤ 1018)

分析:枚举平衡点,数位DP

# include<cstdio>
# include<cstring>
# include<cmath>
# include<algorithm>
using namespace std; # define N
# define LL long long int dig[N];
LL dp[N][N][]; LL dfs(int pos,int central,int pre,int limit)
{
int i,s,end;
LL res=;
if(pos==-)
return pre==;
if(pre<)
return ;
if(!limit&&dp[pos][central][pre]!=-)
return dp[pos][central][pre];
end=limit?dig[pos]:;
for(i=;i<=end;i++)
res+=dfs(pos-,central,pre+(pos-central)*i,limit&&i==end);
if(!limit)
dp[pos][central][pre]=res;
return res;
} LL calc(LL n)
{
if(n<)
return ;
int len=-;
LL res=;
while(n)
{
dig[++len]=n%;
n/=;
}
for(int central=;central<=len;central++) //枚举平衡点
res+=dfs(len,central,,);
return res-len;
} int main()
{
int t;
LL l,r;
memset(dp,-,sizeof(dp));
scanf("%d",&t);
while(t--)
{
scanf("%I64d%I64d",&l,&r);
printf("%I64d\n",calc(r)-calc(l-));
} return ;
  }

HDU 3709

六. HDU 4507吉哥系列故事——恨7不成妻

http://acm.hdu.edu.cn/showproblem.php?pid=4507

题意:求区间[L,R]数字中不满足下列3个条件的数的平方和  (1 <= L <= R <= 10^18)

1、整数中某一位是7;

2、整数的每一位加起来的和是7的整数倍

3、这个整数是7的整数倍;

分析:若求不满足上面条件的数字,简单的数位DP可以解决!但是我们我们要求平方和,则需要维护3个值 (不满足条件的数的个数,这些数的和,这些数的平方和)

如何来求平方和:如果我们求(ab)^2 则求(a*10+b)^2

(ab)^2=(a*10+b)^2

令x=a*10,y=b

则 (x+y)^2=x^2+y^2+2^x^y;

假设枚举到当前pos位时,我们选择的是数8(即x=8),再假设8带头的后面有12,13,是满足条件的,即812,813,那么我们要计算 812^2+813^2

812^2+813^2=(8*100+12)^2+(8*100+13)^2=2*800^2+12^2+13^3+2*800*(12+13)

2*800^2可以直接计算,而12^2+13^2已经通过递归维护好了,而12+13就是我们维护的这些数的和 所以,枚举到当前位的平方和=2*当前这个数*以该位开始满足条件的数的个数+以该位开始满足条件的那些数的平方和+2*当前数*以该位开始满足条件的数的和(而这些数的和又可以通过类似的方法进行维护)

所以一次得返回3个值,可以用返回一个存储3个值的节点(起初我用3个数位DP分别维护3个值)

# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std; # define N
# define LL long long
# define MOD struct node
{
LL c,s1,s2; //个数 连续和 连续平方和
}; node dp[N][N][N];
LL base[N];
int dig[N]; LL mul(LL a,LL b)
{
return ((a%MOD)*(b%MOD))%MOD;
} node dfs(int pos,int pre1,int pre2,int limit)
{
int i,s,end;
node cnt,next;
if(pos==-)
{
cnt.c=(!pre1||!pre2)?:;
cnt.s1=cnt.s2=;
return cnt;
}
if(!limit&&dp[pos][pre1][pre2].c!=-)
return dp[pos][pre1][pre2];
end=limit?dig[pos]:;
cnt.c=cnt.s1=cnt.s2=;
for(i=;i<=end;i++)
{
if(i==)
continue;
next=dfs(pos-,(pre1+i)%,(pre2*+i)%,limit&&i==end);
cnt.c=(cnt.c+next.c)%MOD;
cnt.s1=(cnt.s1+mul(mul(i,base[pos]),next.c)+next.s1)%MOD;
LL a=mul(i,base[pos]),x=mul(a,a);
cnt.s2=(cnt.s2+mul(x,next.c)+next.s2+*mul(a,next.s1))%MOD;
}
if(!limit)
dp[pos][pre1][pre2]=cnt;
return cnt;
} void init()
{
int i,j,k;
base[]=;
for(i=;i<N;i++)
base[i]=(base[i-]*)%MOD;
for(i=;i<N;i++)
for(j=;j<N;j++)
for(k=;k<N;k++)
dp[i][j][k].c=-;
} LL calc(LL n)
{
int len=-;
while(n)
{
dig[++len]=n%;
n/=;
}
return dfs(len,,,).s2;
} int main()
{
init();
int t;
LL l,r,ans;
// freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
// freopen("C:\\Users\\Administrator\\Desktop\\out.txt","w",stdout);
scanf("%d",&t);
while(t--)
{
scanf("%I64d%I64d",&l,&r);
ans=calc(r)-calc(l-);
printf("%I64d\n",(ans%MOD+MOD)%MOD);
}
return ;
  }

HDU 4507

七. SPOJ 10606   Balanced Numbers

http://www.spoj.com/problems/BALNUM/

题意:求区间[A,B]数字中每个偶数数字出现奇数次,每个奇数数字出现偶数次的数的个数1 <= A <= B <= 1019

分析:用3进制表示当前状态,对于0-9的每个数字,0:没出现过,1:出现奇数次 2:出现偶数次   则每个状态都可以表示成一个3进制数(注意前导0)

# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std; # define LL long long
# define N
LL dp[N][];
int dig[N],vis[N],base[N]; bool check()
{
int i;
for(int i=;i<=;i++)
if(vis[i])
{
if(i&&&vis[i]&)
return ;
else if(!(i&)&&!(vis[i]&))
return ;
}
return ;
} LL dfs(int pos,int add,int statu,int limit)
{
int i,s,end,X;
LL res=;
if(pos==-)
return check()&&statu;
if(!limit&&dp[pos][add]!=-)
return dp[pos][add];
end=limit?dig[pos]:;
for(i=;i<=end;i++)
{
s=statu;
if(s==&&i==)
s=;
else
s=;
int flag=;
X=add;
if(s)
{
if(vis[i]==)
flag=,vis[i]=,X=add+base[i];
else if(vis[i]==)
flag=,vis[i]=,X=add+base[i];
else
flag=,vis[i]=,X=add-base[i];
}
res+=dfs(pos-,X,s,limit&&i==end);
if(s)
vis[i]=flag;
}
if(!limit)
dp[pos][add]=res;
return res;
} LL calc(LL n)
{
int len=-;
memset(vis,,sizeof(vis));
while(n)
{
dig[++len]=n%;
n/=;
}
return dfs(len,,,);
} int main()
{
int i,t;
LL l,r;
base[]=;
for(i=;i<=;i++)
base[i]=base[i-]*;
memset(dp,-,sizeof(dp));
scanf("%d",&t);
while(t--)
{
scanf("%lld%lld",&l,&r);
printf("%lld\n",calc(r)-calc(l-));
}
return ;
  }

SPOJ 10606

八. URAL 1057 Amount of Degrees

题意:求区间[x,y]内,有多少个数可以分解成K个不同B的次方  (1 ≤ X ≤ Y ≤ 231−1).

(1 ≤ K ≤ 20; 2 ≤ B ≤ 10).

分析:先将数字表示成B进制, 然后数位DP,枚举每一位的时候注意不是0就是1,因为要要求k个不同的B进制相加 最后判断下是否刚好有k个即可

# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std; # define LL __int64
# define N
int dig[N];
int dp[N][N]; int dfs(int pos,int statu,int k,int limit)
{
int i,s,end,res=;
if(pos==-)
return statu==k;
if(!limit&&dp[pos][statu]!=-)
return dp[pos][statu];
end=limit?dig[pos]:;
for(i=;i<=end;i++)
{
if(i>)
break;
res+=dfs(pos-,statu+i,k,limit&&i==end);
}
if(!limit)
dp[pos][statu]=res;
return res;
} int calc(int n,int k,int b)
{
int len=-;
while(n)
{
dig[++len]=n%b;
n/=b;
}
return dfs(len,,k,);
} int main()
{
// freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
// freopen("C:\\Users\\Administrator\\Desktop\\out.txt","w",stdout);
int x,y,k,b;
while(scanf("%d%d%d%d",&x,&y,&k,&b)!=EOF)
{
memset(dp,-,sizeof(dp));
printf("%d\n",calc(y,k,b)-calc(x-,k,b));
}
return ;
  }

ural 1057

4.更多题目(未解决)

1. codeforces 55d / SPOJ JZPEXT

2. HDU 4352

3. SGU 258

4. SPOJ 1182 Sorted bit sequence

5. HDU 3271 SNIBB

6. SPOJ 2319 Sequence

7. SGU 390 Tickets

[DP]数位DP总结的更多相关文章

  1. 数位dp模板 [dp][数位dp]

    现在才想到要学数位dp,我是不是很弱 答案是肯定的 以一道自己瞎掰的题为模板 //题: //输入数字n //从0枚举到n,计算这n+1个数中含有两位数a的数的个数 //如12930含有两位数93 #i ...

  2. 【xsy1611】 数位dp 数位dp

    这题是显然的数位$dp$,然而我居然写了一个下午!!! 我们不难想到差分,令$solve(x,y)$表示从第一个数字在区间$[0,x]$,第二个数字在区间$[0,y]$的答案. 不难发现题目中给了你一 ...

  3. Codeforces Round #235 (Div. 2) D. Roman and Numbers 状压dp+数位dp

    题目链接: http://codeforces.com/problemset/problem/401/D D. Roman and Numbers time limit per test4 secon ...

  4. hdu4352-XHXJ's LIS状压DP+数位DP

    (有任何问题欢迎留言或私聊 && 欢迎交流讨论哦 题意:传送门  原题目描述在最下面.  在区间内把整数看成一个阿拉伯数字的集合,此集合中最长严格上升子序列的长度为k的个数. 思路: ...

  5. luogu P4798 [CEOI2015 Day1]卡尔文球锦标赛 dp 数位dp

    LINK:卡尔文球锦标赛 可以先思考一下合法的序列长什么样子. 可以发现后面的选手可以使用前面出现的编号也可以直接自己新建一个队. 其实有在任意时刻i 序列的mex>max.即要其前缀子序列中1 ...

  6. 浅谈数位DP

    在了解数位dp之前,先来看一个问题: 例1.求a~b中不包含49的数的个数. 0 < a.b < 2*10^9 注意到n的数据范围非常大,暴力求解是不可能的,考虑dp,如果直接记录下数字, ...

  7. 数位dp 的简单入门

    时间紧张,就不讲那么详细了. 之前一直被深搜代码误解,以为数位dp 其实就是记忆化深搜...(虽说爆搜确实很舒服而且还好想) 但是后来发现数位dp 的标准格式其实是 预处理 + dp ...... 数 ...

  8. 【距离GDOI:141天】 滚入数位DP的坑

    作为博客园的第一篇...我都不知道要写什么了 ... 其实今天很没状态,就当吐槽吧... 嗯,被黄神带去写treap+可持久化线段树,然后在可持久化的删除上面跪了两天,真的是一跪不起.我已经连续多久没 ...

  9. 数位dp介绍

    不了解dp的可以先看一下dp 数位dp含义: 数位:一个数有个位,十位,百位,千位等等,数的每一位都是数位. 数位dp归为计数dp,是在数位上进行操作的dp. 数位dp的实质是一种快速枚举的方式,它满 ...

随机推荐

  1. Html5新标签解释及用法

    Html5新标签解释及用法 HTML 5 是一个新的网络标准,目标在于取代现有的 HTML 4.01, XHTML 1.0 and DOM Level 2 HTML 标准.它希望能够减少浏览器对于需要 ...

  2. JDE910笔记1--基础介绍及配置

    1.一般JDE部署后环境: DV:开发环境 PY:测试环境 PD:正式环境 根据端口号区分不同环境,可配置.同时,JDE默认使用分发服务器,不同环境连接为不同的数据库. 2.命名规范: 自定义项目.函 ...

  3. mke2fs/mks.etc3/fstab/mount指令

    一.mke2fs指令mkfs.etc3 /dev/sdb1指令 主要新学习 cat /etc/filesystem  //查看文件类型 mkfs. tab键有提示    //按照系统默认的值格式化 m ...

  4. qml支持多平台的编译--尤其对于需要支持xp的情况

    http://www.oschina.net/p/deepin-boot-maker 系统支持: Windows平台: Windows 7/ Windows 8 需要安装显卡驱动 Windows XP ...

  5. heartbeat安装

    wget ftp://mirror.switch.ch/pool/1/mirror/scientificlinux/6rolling/i386/os/Packages/epel-release-6-5 ...

  6. ionicModal中的监听事件

    //添加监听事件angular.module('MyApp').directive('gotTapped', ['$ionicGesture', function($ionicGesture) { r ...

  7. 数据结构-List

    Lis的实现: /////////////////////////////////////////////////////////////////////////////// // // FileNa ...

  8. wp8.1 Study14 FilePicker简单介绍

    一.FileOpenPicker/FileSavePicker介绍 这个在使用手机中是十分经常的,如在朋友圈中你要发图片,首先点击添加图片,而后你会进入手机图片区,选择图片后自动返回朋友圈准备发图界面 ...

  9. 关于Integer缓冲

    默认的时候,-128--127范围内的数会被缓冲 但是jvm启动的时候可以修改启动参数-Djava.lang.Integer.IntegerCache.high=2000 来修改上限,但是下限是不可以 ...

  10. 踏着前人的脚印学hadoop——ipc中的Server

    1.An abstract IPC service.  IPC calls take a single {@link Writable} as a parameter, and return a {@ ...