(Sol)
数位(dp)常规套路题.
(dp[i][j])表示从低位到高位填到第(i)位且第(i)位的数字为(j)的方案数.答案就是(sol(r)-sol(l+1).)这里(dp)的过程十分简单,一般出错的也就是计算小于等于(x)的(windy)数,所以这里简述一下这个流程:
首先是预处理出(x)的位数(ct),(a[i])表示数(x)从低到高第(i)位的数字是多少,然后累计第一种安全态的答案(as+=sum _{i=1}^{ct-1}sum _{j=1}^{9}f[i][j]).
接着累计第二种安全态的答案,也就是第(ct)位(从低到高)的数小于(a[ct])的,(as+=sum_{j=1}^{a[ct]-1}f[ct][j]).
最后也就是最容易出错的地方便是统计危险态的答案.假设当前填到第(i)位且(i)到(ct)的数字都和(x)一样,那么第(i-1)位上填的数就有两个限制,一是要小于(a[i-1]),还有就是要与(a[i])的差的绝对值要大于等于(2).
会发现上面的统计有两个漏洞,一是如果数(x)是(windy)数,那么会少算一个,这个问题很好解决,判断一下就好.令一个问题是假设当前填到(i),且(abs(a[i]-a[i-1])<=2),那么下一位就不能填(a[i-1]),那么也就不用继续统计危险态的答案了,直接(break)掉.
(Code)
Code
```cpp
#include
#define il inline
#define Ri register int
#define go(i,a,b) for(Ri i=a;i<=b;++i)
#define yes(i,a,b) for(Ri i=a;i>=b;--i)
#define e(i,u) for(Ri i=b[u];i;i=a[i].nt)
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
#define db double
#define inf 2147483647
using namespace std;
il int read()
{
Ri x=0,y=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*y;
}
int f[20][10],a[20];
il void init()
{
go(i,0,9)f[1][i]=1;
go(i,2,10)
go(j,0,9)
go(k,0,9)
if(abs(j-k)>=2)f[i][j]+=f[i-1][k];
}
il int sol(Ri x)
{
if(!x)return 0;
Ri qvq=x,ct=0,ret=0;bool fl=1;
while(qvq){a[++ct]=qvq%10;qvq/=10;}
go(i,1,ct-1)
go(j,1,9)ret+=f[i][j];
go(i,1,a[ct]-1)ret+=f[ct][i];
yes(i,ct,2)
{
go(j,0,a[i-1]-1)
{
if(abs(j-a[i])>=2)ret+=f[i-1][j];
}
if(abs(a[i]-a[i-1])<2){fl=0;break;}
}
return ret+fl;
}
int main()
{
init();
Ri l=read(),r=read();
printf("%d
",(sol(r)-sol(l-1)));
return 0;
}
```