几道数位DP
因为这几天写的几道数位DP大多都太水。。而且也确实没什么好讲所以就扔到一起了。
[hdu4772]Good Numbers
要求统计区间内 各位数之和能被10整除 的数的个数。
练手,f[i][j][k]表示i位的数,以j开头,各位数之和取模10的结果为k,的方案数。
#include<cstdio>
#include<iostream>
#include<cstring>
#define ll long long
using namespace std;
int i,j,j1,k,k1,n,m;
ll f[][][],ten[],l,r; int s[],len;
inline ll get(ll x){
if(x<)return ;
int i,j,k,pre;ll tmp,ans=;
for(i=;i<=;i++)if(x==ten[i]){x--;break;}
if(!x)return ;
for(tmp=x,len=;tmp;tmp/=)s[++len]=tmp%; for(i=;i<len;i++)for(j=;j<=;j++)ans+=f[i][j][];
for(i=;i<s[len];i++)ans+=f[len][i][]; for(pre=s[len],i=len-;i;i--){
k=(-pre+)%;
for(j=;j<s[i];j++)ans+=f[i][j][k];
pre=(pre+s[i])%;
}
return ans+(pre==)+;
}
int main(){
for(i=ten[]=;i<=;i++)ten[i]=ten[i-]*;
for(i=;i<=;i++)f[][i][i]=;
for(i=;i<=;i++)
for(j=;j<=;j++)for(k=;k<=;k++)
for(j1=,k1=(k-j+)%;j1<=;j1++)f[i][j][k]+=f[i-][j1][k1]; for(scanf("%d",&m),n=;n<=m;n++){
scanf("%lld%lld",&l,&r);
printf("Case #%d: %lld\n",n,get(r)-get(l-));
}
return ;
}
[hdu2089]不要62
要求统计区间内有多少个数 不包含62并且不包括4.
f[i][j]表示i位的数,以j开头,非法数字的个数。
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int f[][],ten[],sum;
int i,j,k,n,m;
int now[];
inline int get(int x){
if(!x)return ;
int i,len=,ans=;
for(int tmp=x;tmp;tmp/=)now[++len]=tmp%;
now[len+]=;
for(i=len;i;i--){
for(j=;j<now[i];j++){
ans+=f[i][j];//,printf("add: %d %d\n",i,j);
if(j==&&now[i+]==)ans+=ten[i-]-f[i][j];
}
if(now[i]==||(now[i]==&&now[i+]==)){ans+=x%ten[i-]+;break;}
}
// printf("%d ans:%d\n",x,ans);
return ans;
}
int main(){
for(i=;i<=;i++)f[][i]=i==;
ten[]=,ten[]=;
for(i=;i<=;i++){
ten[i]=ten[i-]*;
for(j=sum=;j<=;j++)sum+=f[i-][j];
for(j=;j<=;/*printf("%d %d %d\n",i,j,f[i][j]),*/j++)
if(j==)f[i][j]=ten[i-];
else if(j==)f[i][j]=sum+ten[i-]-f[i-][];
else f[i][j]=sum;
}
while(scanf("%d",&n)==){
scanf("%d",&m);
if(!n&&!m)break;
printf("%d\n",m-n+-(get(m)-get(n-)));
}
return ;
}
[hdu3555]bomb
要求统计区间内有多少个包含49的数。
f[i][j]表示i位的数,j开头,包含49的数的个数。(因为49这个数比较特殊一点(9是最大的数)...统计的时候可以不写关于第二位数的特判)
#include<cstdio>
#include<iostream>
#include<cstring>
#define ll long long
using namespace std;
ll f[][],ten[];
int i,j,len,s[];
ll tmp,n;
inline ll get(ll x){
ll ans=;
for(len=,tmp=x;tmp;tmp/=)s[++len]=tmp%;s[len+]=;
for(i=;i<len;i++)for(j=;j<=;j++)ans+=f[i][j];
for(i=len;i;i--){
for(j=i==len;j<s[i];j++)ans+=f[i][j];
if(s[i]==&&s[i+]==){ans+=x%ten[i-]+;break;}
}
return ans;
}
int main(){
for(i=ten[]=;i<=;i++)ten[i]=ten[i-]*(ll);
for(i=;i<=;i++)
for(j=;j<=;/*printf("%d %d %lld\n",i,j,f[i][j]),*/j++)
if(j!=&&i>)f[i][j]=f[i-][j]*+f[i-][];
else f[i][j]=f[i-][]*+f[i-][]+ten[i-];
int T;
for(scanf("%d",&T);T;T--)
scanf("%lld",&n),printf("%lld\n",get(n));
return ;
}
[hdu4507]恨7不成妻
预处理的时候,数字的平方和可以通过 方案数、数字和、数字的平方和 三个玩意算出来。。算是常见姿势了吧。
再具体点见代码注释
#include<cstdio>
#include<iostream>
#include<cstring>
#define ll long long
using namespace std;
const int modd=;
struct zs{
ll num,sum,sqr;//方案数,方案的数字和,平方和
}f[][][][][];//f[i][x][j][k][l]:i位的数,开头数字为x,j表示有无某一位为7,k表示每一位加起来的和%7的值,l表示数字%7的值。
ll ten[];
ll l,r; inline void add(zs &a,zs b,ll num){
num%=modd;
a.num+=b.num,a.sum+=(b.sum+num*b.num)%modd,
a.sqr+=b.sqr+(b.sum*num%modd*)+(b.num*num%modd*num%modd),
a.num-=a.num>=modd?modd:,a.sum-=a.sum>=modd?modd:,
a.sqr%=modd;
} int s[],len;
inline ll get(ll num){
if(num==)return ;
register int i,j,k,x,pre2;ll tmp,ans=,pre,pre1;zs b; for(tmp=num,len=;tmp;tmp/=)s[++len]=tmp%;
for(i=;i<len;i++)for(x=;x<=;x++)for(j=;j<;j++)for(k=;k<;k++)ans+=f[i][x][][j][k].sqr,ans-=ans>=modd?modd:;
for(x=;x<s[len];x++)for(j=;j<;j++)for(k=;k<;k++)ans+=f[len][x][][j][k].sqr,ans-=ans>=modd?modd:; pre=s[len]*ten[len-]%modd,pre1=s[len]*ten[len-]%,pre2=s[len]%;
if(s[len]!=)
for(i=len-;i;i--){
// printf(" i:%d pre1:%lld pre2:%d pre:%lld\n",i,pre1,pre2,pre);
for(x=;x<s[i];x++)for(j=;j<;j++)for(k=;k<;k++)
b=f[i][x][][(j-pre2+)%][(k-pre1+)%],
ans=(ans+b.sqr+b.sum*pre%modd*+b.num*pre%modd*pre%modd)%modd;
pre=(pre+s[i]*ten[i-])%modd;pre1=(pre1+s[i]*ten[i-])%;pre2=(pre2+s[i])%;
if(s[i]==)break;
}
return ans;
}
int main(){
register int i,j,k,j1,k1,x;ll num; for(i=ten[]=;i<=;i++)ten[i]=ten[i-]*;
for(i=;i<=;i++)k=i%,f[][i][i==][k][k]=(zs){,i,i*i};
for(i=;i<=;i++)
for(j=;j<;j++)for(k=;k<;k++)
for(x=num=;x<=;x++,num+=ten[i-]){
j1=j-x%,j1+=j1<?:,k1=k-(num%),k1+=k1<?:;
if(x!=)
for(int x1=;x1<=;x1++)add(f[i][x][][j][k],f[i-][x1][][j1][k1],num);
else
for(int x1=;x1<=;x1++)add(f[i][x][][j][k],f[i-][x1][][j1][k1],num);
for(int x1=;x1<=;x1++)add(f[i][x][][j][k],f[i-][x1][][j1][k1],num);
} int T;
for(scanf("%d",&T);T;T--){
scanf("%lld%lld",&l,&r);
printf("%lld\n",(get(r+)-get(l)+modd)%modd);
}
return ;
}
[hdu3652]B-number
要求出区间内包含13,并且能被13整除的数的个数。
f[i][j][k][l]表示i位数,开头为j,k表示是否包含13,l表示数字模13后的结果,的方案数
#include<cstdio>
#include<iostream>
#include<cstring>
#define ll long long
using namespace std;
int ten[];
int f[][][][];
int i,j,k,j1,k1,n,m; int s[],len;
inline int get(int x){
if(x<)return ;
register int i,j,k,j1,k1,ans=,pre;int tmp;
for(len=,tmp=x;tmp;tmp/=)s[++len]=tmp%;
for(i=;i<len;i++)for(j=;j<=;j++)ans+=f[i][j][][];
for(i=;i<s[len];i++)ans+=f[len][i][][]; pre=(s[len]*ten[len-])%;bool flag=;
for(i=len-;i;i--){
k=(-pre+)%;
if(!flag)
for(j=;j<s[i];j++)ans+=f[i][j][][k];
else
for(j=;j<s[i];j++)ans+=f[i][j][][k]+f[i][j][][k];
if(!flag&&s[i+]==&&s[i]>)ans+=f[i][][][k];
if(s[i]==&&s[i+]==)flag=;
pre=(pre+s[i]*ten[i-])%;
}
if(flag)ans+=pre==;
return ans;
}
int main(){
for(i=ten[]=;i<=;i++)ten[i]=ten[i-]*;
for(i=;i<=;i++)f[][i][][i]=;
for(i=;i<=;i++)for(j=;j<=;j++){ for(k=;k<;k++){
for(j1=,k1=(k-j*ten[i-]%+)%;j1<=;j1++){
if(j!=||j1!=) f[i][j][][k]+=f[i-][j1][][k1];
else f[i][j][][k]+=f[i-][j1][][k1];
f[i][j][][k]+=f[i-][j1][][k1];
}
// printf(" %d %d %d %d\n",i,j,k,f[i][j][1][k]);
}
}
while(scanf("%d",&n)==)
printf("%d\n",get(n)); return ;
}
[hdu3709]Balanced Number
无力去膜题解。http://blog.csdn.net/acm_cxlove/article/details/7821726
顺便感受了一下记忆化搜索在处理边界等方面的优越性= =
感觉for的话就是负数不太好处理。
#include<cstdio>
#include<iostream>
#include<cstring>
#define ll long long
using namespace std;
int i,j,k,n,m;
int s[],len;ll tmp,l,r;
ll f[][][],ten[];
bool u[][][]; inline ll dfs(int x,int pos,int pre,bool limit){//考虑剩下的x位数字,支点已确定在pos
if(!x||pre<) return pre==;
if(!limit&&u[x][pos][pre])return f[x][pos][pre]; int mx=limit?s[x]:;ll ans=;
for(int i=;i<=mx;i++)
ans+=dfs(x-,pos,pre-i*(pos-x),limit&&(i==mx));
if(!u[x][pos][pre]&&!limit)
f[x][pos][pre]=ans,u[x][pos][pre]=;
return ans;
}
inline ll get(ll x){
ll ans=;
for(int i=;i<=;i++)
if(x==ten[i]){x--,ans++;break;}
if(x<)return ; for(len=,tmp=x;tmp;tmp/=)s[++len]=tmp%;
for(int i=len;i;i--)
ans+=dfs(len,i,,);
return ans-len+;
}
int main(){
for(i=ten[]=;i<=;i++)ten[i]=ten[i-]*;
int T;
for(scanf("%d",&T);T;T--){
scanf("%lld%lld",&l,&r);
printf("%lld\n",get(r)-get(l-));
}
return ;
}
UPD:其实以上写法中,f 数组的“以数字j开头”的那维都是不必要的。。。(所以我个傻逼预处理比别人多了两重循环TAT
几道数位DP的更多相关文章
- HDU2089 不要62[数位DP]
不要62 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submis ...
- 浅谈数位DP
在了解数位dp之前,先来看一个问题: 例1.求a~b中不包含49的数的个数. 0 < a.b < 2*10^9 注意到n的数据范围非常大,暴力求解是不可能的,考虑dp,如果直接记录下数字, ...
- hdu5642 数位dp
题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5642 题意:一个长度为n的序列,合法序列为字符中不能出现长度大于3的连续相等的字符,求一共 ...
- 动态规划晋级——HDU 3555 Bomb【数位DP详解】
转载请注明出处:http://blog.csdn.net/a1dark 分析:初学数位DP完全搞不懂.很多时候都是自己花大量时间去找规律.记得上次网络赛有道数位DP.硬是找规律给A了.那时候完全不知数 ...
- Ural1057 - Amount of Degrees(数位DP)
题目大意 求给定区间[X,Y]中满足下列条件的整数个数:这个数恰好等于K个互不相等的B的整数次幂之和.例如,设X=15,Y=20,K=2,B=2,则有且仅有下列三个数满足题意: 输入:第一行包含两个整 ...
- 【HDU 3652】 B-number (数位DP)
B-number Problem Description A wqb-number, or B-number for short, is a non-negative integer whose de ...
- 【数位DP】【HDU2089】不要62
不要62 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submi ...
- hdoj2089(入门数位dp)
题目链接:https://vjudge.net/problem/HDU-2089 题意:给定一段区间求出该区间中不含4且不含连续的62的数的个数. 思路:这周开始做数位dp专题,给自己加油^_^,一直 ...
- Luogu3220 HNOI2012 与非 数位DP
传送门 题意:给出$N$个范围在$[0,2^k-1]$的整数,定义位运算$NAND$为位运算$AND$的逆运算,求$[L,R]$中有多少数能成为若干个前面给出的整数.若干括号和$NAND$运算组成的表 ...
随机推荐
- ArcGIS API for JavaScript 4.2学习笔记[11] 官方第五章Popups(弹窗)概览与解释
直接跳过第三第四章了,第三章Layer和第四章可视化,怎么说呢,Layer是组织数据的,是Map的属性之一.可视化属于符号化编程,暂时不看. 第五章是对数据.结果的显示,类似于alert()..NET ...
- Java-单例模式详解(图文并茂,简单易懂)
PS:首先我们要先知道什么是单例,为什么要用单例,用的好处是什么等问题来看. 1:java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍两种:懒汉式单例.饿汉式单例单例模式有以下 ...
- 30分钟入门Java
技术只是工具,文档只是说明书,仅此而已. 写在前面 工作4年有余,盲人摸象般的走过弯路,也投机取巧的领悟到过一些类似"编程本质"的东西.现在开始我计划回顾下我的编程生涯.在这里分享 ...
- Nodejs使用redis
安装方法 安装redis方法请自行百度, 用npm方法,安装nodejs的redis模块 npm install redis 实战 var redis = require("redis&qu ...
- jquery append 动态添加的元素绑定事件on
用jquery添加新元素很容易,面对jquery append 动态添加的元素事件on 不起作用我们该如何解决呢?on方法中要先找到原选择器(如例.info),再找到动态添加的选择器(如列.delet ...
- [js高手之路] vue系列教程 - vue的基本用法与常见指令(1)
本系列课程选用vue的版本为1.0.21, 什么是vue? vue是由尤雨溪开发的一款基于MVVM的框架,M->模型,V->视图, 也就是说模型数据改变了,视图也跟着改变, 视图内容改变, ...
- win8使用putty登录虚拟机linux
从下午两点开始在尝试,差不多用了6个小时候到现在终于成功了! 连接器使用的是putty,只要知道虚拟机的ip地址就可以尝试连接,所以首先查询虚拟机上的ip地址,使用命令: ifconfig 出现提示: ...
- 设计模式学习(三): 装饰者模式 (附C#实现)
需求 做一个咖啡店的订单系统. 买咖啡时,可以要求加入各种调料,如奶,豆浆,摩卡等.咖啡店会根据调料的不同收取不同的费用.订单系统要考虑这些. 初版设计 然后下面就是所有的咖啡....: cost方法 ...
- 设计模式成长记(一) 抽象工厂模式(Abstract Factory)
目录 定义 UML类图 参与者 编写代码 特点 定义 提供一个创建一系列相关或相互依赖的对象的接口,而无需指定具体的类. 使用频率: UML类图 参与者 AbstractFactory:声明一个创建抽 ...
- Oracle之 11gR2 RAC 修改监听器端口号的步骤
Oracle 11gR2 RAC 修改监听器端口号的步骤 说明:192.168.188.181 为public ip1192.168.188.182 为public ip2192.168.188.18 ...