Description
windy定义了一种windy数。不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。 windy想知道,
在A和B之间,包括A和B,总共有多少个windy数?
Input
包含两个整数,A B。
Output
一个整数
Sample Input
1 10
【输入样例二】
25 50
Sample Output
9
【输出样例二】
20
HINT
【数据规模和约定】
100%的数据,满足 1 <= A <= B <= 2000000000 。
这是我写的第一道数位dp题,心里好开心>_<
因为网上我找不到什么好的题解,所以我觉得最好我自己再写一篇虽然也不一定写得好其实主要还是我太弱了,看不懂
首先,我们可以预处理出在没有任何限制的情况下的windy数。这里认为最高位是第$1$位,则可以令$dp_{i,j}$表示第$i$位填$j$的方案数,我从低位往高位转移,显然$$dp_{i,j}=\sum_{k=0}^{9} dp_{i+1,k}(| k-j| \ge 2)$$
接下来,我们可以处理出$f_i$表示在第$i$位取该位最大值的方案数。设这个数第$i$位为$a_i$,则转移为$$f_i=\sum_{k=0}^{a_{i+1}-1} dp_{i+1,k}(| a_{i}-k| \ge 2)$$
当然,如果$|a_{i+1}-a_i| \ge 2$ ,$f_i$还要加上$f_{i+1}$。
然后,我们可以考虑如何统计答案。既然要求$[l,r]$之间的windy数个数,那么显然可以转为前缀和的差,也就是区间$[1,r]$的答案减去区间$[1,l-1]$的答案。
那么,我们只需知道小于等于$x$的windy数个数即可。我们来考虑下。
首先,我们可以先找到这个数第一个不为$0$的位置$w$。然后,这个位置显然可以放$1$到$a_w-1$,这部分的答案为$\sum_{i=1}^{a_{w}-1} dp_{w,i}$。然后,当这个位置放$a_w$时方案数就是$f_w$。
接下来我们还要处理掉在位置$w$填$0$的情况。设这个数长度为$len$,我们只需再统计一下$\sum_{i=w+1}^{len} \sum_{j=1}^{9}dp_{i,j}$就可以了。
下面是代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
#define INF 2147483647 using namespace std;
typedef long long llg; int a[][],g[][],n,ans,x,y,z;
int f[]; void init(int x){
int now=,ww=;
while(!a[x][ww] && ww<=n) ww++;
memset(g,,sizeof(g));
memset(f,,sizeof(f)); f[n]=;
for(int i=;i<=;i++) g[n][i]=;
for(int i=n-;i;i--){
for(int j=;j<=;j++)
for(int k=;k<=;k++)
if(abs(j-k)>=) g[i][j]+=g[i+][k];
for(int j=;j<a[x][i+];j++)
if(abs(a[x][i]-j)>=) f[i]+=g[i+][j];
if(abs(a[x][i]-a[x][i+])>=) f[i]+=f[i+];
}
for(int i=ww+;i<=n;i++)
for(int j=;j<=;j++) now+=g[i][j];
if(ww<=n){
for(int i=;i<a[x][ww];i++) now+=g[ww][i];
now+=f[ww];
}
if(x) ans+=now; else ans-=now;
} int main(){
File("a");
scanf("%d %d",&x,&y);
x--;z=y; while(z) n++,z/=;
for(int i=n;i;i--){
a[][i]=x%,x/=;
a[][i]=y%,y/=;
}
init(); init();
printf("%d",ans);
return ;
}