[SCOI2009]windy数
[SCOI2009]windy数
题目
windy定义了一种windy数。不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。 windy想知道,在A和B之间,包括A和B,总共有多少个windy数?
INPUT
包含两个整数,A B。
OUTPUT
一个整数
SAMPLE INPUT
1
1 10
2
25 50
SAMPLE OUTPUT
1
9
2
20
解题报告
首道数位$dp$
首先,我们可以把区间和问题转化为前缀和问题,这样就很好处理了
设$f[i][j]$表示第$i$位为$j$的合法数的总数
我们可以得到$f[i][j]=\sum_{ \left | i-k \right |\geq 2}f[i-1][k]$
那么对于一个数$x$,我们可以处理出它的位数以及每一位的数字,对于位数小于该数位数的,我们可以直接求和得到贡献,对于位数相同但首位不同的也是如此,剩下的就是如何统计靠近上界的答案了
我们有该数每一位的数字,那么我们就可以以此为上边界枚举该位数字,判断与前一位关系能否对答案造成贡献,转移$f$数组即可
注意一些细节,比如前导0的处理qwq
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <cmath> 5 #include <algorithm> 6 using namespace std; 7 typedef long long L; 8 L a,b,f[15][15],num[40]; 9 inline L solve(L x){ 10 if(x<=0)return 0; 11 L ret(0);int top(0); 12 while(x){num[++top]=x%10;x/=10;} 13 for(int i=1;i<top;++i) 14 for(int j=1;j<=9;++j) 15 ret+=f[i][j]; 16 for(int i=1;i<num[top];++i)ret+=f[top][i]; 17 for(int i=top-1;i>0;--i){ 18 for(int j=num[i+1]+2;j<=9;++j) 19 if(j<num[i]) 20 ret+=f[i][j]; 21 for(int j=num[i+1]-2;j>=0;--j) 22 if(j<num[i]) 23 ret+=f[i][j]; 24 if(abs(num[i+1]-num[i])<2)break; 25 if(i==1)++ret; 26 } 27 return ret; 28 } 29 int main(){ 30 for(int i=0;i<=9;++i)f[1][i]=1; 31 for(int i=2;i<=11;++i) 32 for(int j=0;j<=9;++j) 33 for(int k=0;k<=9;++k) 34 if(abs(j-k)>=2) 35 f[i][j]+=f[i-1][k]; 36 scanf("%lld%lld",&a,&b); 37 printf("%lld",solve(b)-solve(a-1)); 38 }