[您有新的未分配科技点]数位DP:从板子到基础(例题 bzoj1026 windy数 bzoj3131 淘金)
只会统计数位个数或者某种”符合简单规律”的数并不够……我们需要更多的套路和应用
数位dp中常用的思想是“分类讨论”思想。下面我们就看一道典型的分类讨论例题
1026: [SCOI2009]windy数
Time Limit: 1 Sec Memory Limit: 162 MB
Description
windy定义了一种windy数。不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。 windy想知道,
在A和B之间,包括A和B,总共有多少个windy数?
输入包含两个整数,A和B。
输出一个整数,代表windy数个数
Sample Input
1 10
【输入样例二】
25 50
Sample Output
9
【输出样例二】
20
【数据规模和约定】
100%的数据,满足 1 <= A <= B <= 2000000000 。
题解:
首先转化为典型的work(B+1)-work(A)数数模型(区间左闭右开),下面我们的目标就是求[1,X)内windy数的个数了
设f[i][j]为i位数,第i位是j时windy数的个数。
从样例中我们注意到,个位数都是windy数,那么我们先处理出来,然后从十位数开始预处理,
显然,当abs(j-k)>=2时,f[i][j]+=f[i-1][k];
然后,我们设待处理的数位X=abcdefd(每一位的数字我们用字母表示),设其长度为l
那么对于小于等于x的数(设为t),我们分下面3种情况讨论:
for(int i=1;i<bit[l];i++)ans+=f[l][i];
//1° t的位数与x相同,但t的最高位小于x的最高位:直接加上f[l]["t的最高位"]
for(int i=1;i<b;i++)
for(int j=1;j<10;j++)
ans+=f[i][j];
//2°t的位数比x小,那么任意f[t][j]都是合法的
for(int i=b-1;i;i--)
{
for(int j=0;j<bit[i];j++)
if(abs(j-bit[i+1])>=2)ans+=f[i][j];
if(abs(bit[i]-bit[i+1])<2)break;
}
//3°t的位数与x相同,且t的最高位等于x的最高位
//此时我们就和之前一样,for循环枚举判断即可,注意及时跳出
那么这道题就没有什么大问题了。完整代码见下:
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
LL l,r,f[][];int bit[];
inline int abs(int a){return a>?a:-a;}
inline void intn()
{
memset(f,,sizeof(f));
for(int i=;i<;i++)f[][i]=;
for(int i=;i<=;i++)
for(int j=;j<;j++)
for(int k=;k<;k++)
if(abs(j-k)>=)
f[i][j]+=f[i-][k];
}
inline LL work(LL x)
{
if(x==)return ;
int b=;LL ans=;
memset(bit,,sizeof(bit));
while(x)bit[++b]=x%,x/=;
for(int i=;i<b;i++)
for(int j=;j<;j++)
ans+=f[i][j];
for(int i=;i<bit[b];i++)
ans+=f[b][i];
for(int i=b-;i;i--)
{
for(int j=;j<bit[i];j++)
if(abs(j-bit[i+])>=)ans+=f[i][j];
if(abs(bit[i]-bit[i+])<)break;
}
return ans;
}
int main()
{
intn();
scanf("%lld%lld",&l,&r);
printf("%lld",work(r+)-work(l));
}
接下来这道题可就没有那么简单了……这道题用到了另外一个套路:搜索+找规律
3131: [Sdoi2013]淘金
Time Limit: 30 Sec Memory Limit: 256 MB
Description
小Z在玩一个叫做《淘金者》的游戏。游戏的世界是一个二维坐标。X轴、Y轴坐标范围均为1..N。初始的时候,所有的整数坐标点上均有一块金子,共N*N块。
一阵风吹过,金子的位置发生了一些变化。细心的小Z发现,初始在(i,j)坐标处的金子会变到(f(i),fIj))坐标处。其中f(x)表示x各位数字的乘积,例如f(99)=81,f(12)=2,f(10)=0。如果金子变化后的坐标不在1..N的范围内,我们认为这块金子已经被移出游戏。同时可以发现,对于变化之后的游戏局面,某些坐标上的金子数量可能不止一块,而另外一些坐标上可能已经没有金子。这次变化之后,游戏将不会再对金子的位置和数量进行改变,玩家可以开始进行采集工作。
小Z很懒,打算只进行K次采集。每次采集可以得到某一个坐标上的所有金子,采集之后,该坐标上的金子数变为0。
现在小Z希望知道,对于变化之后的游戏局面,在采集次数为K的前提下,最多可以采集到多少块金子?
答案可能很大,小Z希望得到对1000000007(10^9+7)取模之后的答案。
输入共一行,包含两介正整数N,K。输出为一个整数,表示最多可以采集到的金子数量。
Sample Input
Sample Output
N < = 10^12 ,K < = 100000
对于100%的测试数据:K < = N^2
题解:不难发现,按前几道题的想法完全无法解决本题。
但我们可以发现,12位数的f(i)在去重之后最多有1e4多一些(11026)种不同的情况(很多数的f值都是相等的,可以打表观察出来,一开始深搜可以搜到2*1e5左右)
那么我们就可以深搜+离散一发,把base数组开出来,base[i]表示第i个f值。
之后我们就可以考虑状态的转移了。如果我们设ans[i]为f值等于第i个f值的数的个数,那么显然,对于某个坐标(f(i),f(j)),有i点金块个数=f(i)*f(j)
由于f值等于0是成立的,所以我们预先把0加入f值中,并且规定只能有长度为1的0存在
那么我们定义f数组f[i][j][k],表示枚举到数的第i位(从高位向低位枚举),其乘积为base[j],下一位能不能随意填数(k=1代表不能,k=0代表能)(因为显然坐标在1~n范围内才有贡献)
那么我们可以用lower_bound求出某个数在乘上因子x后在base中的编号然后转移,设转移到的新编号为next,则
f[i+1][next][k+x>a[i+1]]+=f[i][j][k](k+x>a[i+1]表示是否超过范围)
最后对于每个ans[j]把对应的f[i][j][k]统计上即可。代码见下:
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=;
const LL mod=;
LL n;int bit[],b,k,tot;
LL f[][N][],base[N],ans[N];
struct node
{
int x,y;LL val;
node(int a,int b){x=a,y=b,val=ans[a]*ans[b];}
bool operator > (const node &b)const{return val>b.val;}
bool operator < (const node &b)const{return val<b.val;}
};
priority_queue<node>q;
void dfs(int start,int len,LL multi)
{
if(len==b)base[tot++]=multi;
else
{
if(!multi)return;
for(int j=start;j<;j++)
dfs(j,len+,multi*j);
}
}
inline bool mt(const LL &a,const LL &b){return a>b; }
int main()
{
scanf("%lld%d",&n,&k);
b=;memset(bit,,sizeof(bit));
while(n)bit[++b]=n%,n/=;
base[++tot]=;dfs(,,);
sort(base+,base+tot+);
tot=unique(base+,base+tot+)-base-;
base[tot+]=0x7fffffff;
f[][][]=;
for(int i=;i<=b;i++)
for(int j=;j<=tot;j++)
for(int k=;k<=;k++)
if(f[i][j][k])
for(int x=(i==)?:;x<;x++)
{
int next=lower_bound(base+,base+tot+,base[j]*x)-base;
f[i+][next][(k+x)>bit[i+]]+=f[i][j][k];
}
for(int i=;i<=tot;i++)
{
for(int j=;j<b;j++)
ans[i]+=f[j][i][]+f[j][i][];
ans[i]+=f[b][i][];
}
sort(ans+,ans+tot+,mt);
q.push(node(,));
LL ans=;
while(!q.empty()&&k)
{
node t=q.top();q.pop();
ans=(ans+t.val)%mod;
if(!(--k))break;
if(t.x!=t.y)
{
ans=(ans+t.val)%mod;//再加一遍(y,x);
if(!(--k))break;
q.push(node(t.x+,t.y));
}
if(t.x==)q.push(node(t.x,t.y+));
}
printf("%lld",ans);
}
[您有新的未分配科技点]数位DP:从板子到基础(例题 bzoj1026 windy数 bzoj3131 淘金)的更多相关文章
- [您有新的未分配科技点]数位dp:从懵X到板子(例题:HDU2089 不要62)
数位dp主要用来处理一系列需要数数的问题,一般套路为“求[l,r]区间内满足要求的数/数位的个数” 要求五花八门……比如“不出现某个数字序列”,“某种数的出现次数”等等…… 面对这种数数题,暴力的想法 ...
- [您有新的未分配科技点] 无旋treap:从单点到区间(例题 BZOJ1500&NOI2005 维护数列 )
1500: [NOI2005]维修数列 Time Limit: 10 Sec Memory Limit: 64 MB Description Input 输入的第1 行包含两个数N 和M(M ≤20 ...
- [您有新的未分配科技点]博弈论进阶:似乎不那么恐惧了…… (SJ定理,简单的基础模型)
这次,我们来继续学习博弈论的知识.今天我们会学习更多的基础模型,以及SJ定理的应用. 首先,我们来看博弈论在DAG上的应用.首先来看一个小例子:在一个有向无环图中,有一个棋子从某一个点开始一直向它的出 ...
- [您有新的未分配科技点]博弈论入门:被博弈论支配的恐惧(Nim游戏,SG函数)
今天初步学习了一下博弈论……感觉真的是好精妙啊……希望这篇博客可以帮助到和我一样刚学习博弈论的同学们. 博弈论,又被称为对策论,被用于考虑游戏中个体的预测行为和实际行为,并研究他们的应用策略.(其实这 ...
- [您有新的未分配科技点]无旋treap:从好奇到入门(例题:bzoj3224 普通平衡树)
今天我们来学习一种新的数据结构:无旋treap.它和splay一样支持区间操作,和treap一样简单易懂,同时还支持可持久化. 无旋treap的节点定义和treap一样,都要同时满足树性质和堆性质,我 ...
- [您有新的未分配科技点]可,可,可持久化!?------0-1Trie和可持久化Trie普及版讲解
这一次,我们来了解普通Trie树的变种:0-1Trie以及在其基础上产生的可持久化Trie(其实,普通的Trie也可以可持久化,只是不太常见) 先简单介绍一下0-1Trie:一个0-1Trie节点只有 ...
- [您有新的未分配科技点][BZOJ3545&BZOJ3551]克鲁斯卡尔重构树
这次我们来搞一个很新奇的知识点:克鲁斯卡尔重构树.它也是一种图,是克鲁斯卡尔算法求最小生成树的升级版首先看下面一个问题:BZOJ3545 Peaks. 在Bytemountains有N座山峰,每座山峰 ...
- bzoj1026 windy数 数位DP
windy定义了一种windy数.不含前导零且相邻两个数字之差至少为2的正整数被称为windy数. windy想知道,在A和B之间,包括A和B,总共有多少个windy数? Input 包含两个整数,A ...
- 数位dp——BZOJ1026 Windy数
1026: [SCOI2009]windy数 Time Limit: 1 Sec Memory Limit: 162 MB Description windy定义了一种windy数.不含前导零且相邻 ...
随机推荐
- 人脸识别引擎SeetaFaceEngine中Alignment模块使用的测试代码
人脸识别引擎SeetaFaceEngine中Alignment模块用于检测人脸关键点,包括5个点,两个眼的中心.鼻尖.两个嘴角,以下是测试代码: int test_alignment() { std: ...
- 【BZOJ2754】[SCOI2012]喵星球上的点名
[BZOJ2754][SCOI2012]喵星球上的点名 题面 bzoj 洛谷 题解 这题有各种神仙做法啊,什么暴力\(AC\)自动机.\(SAM\)等等五花八门 我这个蒟蒻在这里提供一种复杂度正确且常 ...
- ELKStack入门篇(二)之Nginx、Tomcat、Java日志收集以及TCP收集日志使用
1.收集Nginx的json格式日志 1.1.Nginx安装 [root@linux-node1 ~]# yum install nginx -y [root@linux-node1 ~]# vim ...
- 【转】bash: ssh: command not found解决方法(linux)
原文转自:http://www.cnblogs.com/ahauzyy/archive/2013/04/25/3043699.html 今天在搭建hadoop的开发环境中,用的是centsos6.0的 ...
- Python中的矩阵操作
Numpy 通过观察Python的自有数据类型,我们可以发现Python原生并不提供多维数组的操作,那么为了处理矩阵,就需要使用第三方提供的相关的包. NumPy 是一个非常优秀的提供矩阵操作的包.N ...
- Django的简介
一.MTV模型 Django的MTV模式: Model(模型):和数据库相关的.负责业务对象与数据库的对象(ORM) Template(,模板):放所有的HTML文件 模板语法:目的是将变量(数据库内 ...
- 设计模式C++实现
准备写一系列笔记用来记录学习设计模式的过程,同时写出自己对几种主要的设计模式的理解,以及编码实现,同时总结. 主要参考书籍就是 <Head First Design Patterns>这本 ...
- 算法笔记(c++)--使用一个辅助栈排列另一个栈
算法笔记(c++)--使用一个辅助栈排列另一个栈 仅仅使用一个辅助栈,不使用其他数据结构来排列一个栈,要求,上大下小. 分析下.肯定是先吧主栈中的数据都放到辅助栈中,在辅助栈中上小下大. 1.首先循环 ...
- Method 'ExecuteAsync' in type 'System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy' does not have an implementation
一.错误信息 Entity Framework 6.0数据迁移:Add-Migration XXXX 命令发生错误 System.Reflection.TargetInvocationExceptio ...
- 获取秒级时间戳和毫秒级时间戳---基于python
获取秒级时间戳和毫秒级时间戳 import timeimport datetime t = time.time() print (t) #原始时间数据print (int(t)) #秒级时间戳prin ...