P2657 【[SCOI2009]windy数】 数位DP板子题

转自 https://www.luogu.com.cn/blog/ljc20020730/solution-p2657

一起学下数位dp

  • 设f(x)表示x前面的数 即 t∈[1,x) 中windy数的个数

那么显然如果要求 [l,r]中windy数的个数就是:

  • F(l,r) = f(r+1)-f(l)

数位dp开始,预处理位数为i最高位为j的windy数个数 f[i][j]

转移:

  • f[i][j]=f[i-1][k] | 其中k是非负整数k∈[0,9]且|k-j|>=2

初始值:

  • f[1][i]=1 | 其中i为非负整数 i∈[0,9]

求f(x)怎么求呢?

为了方便处理先对数x进行按10进制位拆分到a[]数组

  • 显然位数比x要小的数字都是合法的都在[1,x)区间内,直接统计就行
  • 位数和x一样最高位的数字比x小的数字都是合法的都在[1,x)区间内直接统计就行
  • 位数和x一样,最高位又和x一样我们从左到右扫一遍x各个位子上的数字大小然后枚举合法的该位子上的数[0,9]判断是否合法就行。
ll f[15][15];
int a[15];
ll fun(ll x)
{
    int len=0;
    while (x>0ll) a[++len]=x%10,x/=10;
    ll sum=0ll;
    for (int i=1;i<=len-1;i++)
     for (int j=1;j<=9;j++)
      sum+=f[i][j];
    for (int i=1;i<a[len];i++) sum+=f[len][i];
//这最后一个循环 其实没包括X这个数本身啊 算的是1~x-1的 所以下面调用的时候是m+1
for (int i=len-1;i>=1;i--) { for (int j=0;j<=a[i]-1;j++) if (abs(j-a[i+1])>=2) sum+=f[i][j]; if (abs(a[i+1]-a[i])<2) break; //这一位不行后面的位子一定不行 } return sum; } int main() { ll n,m; scanf("%lld %lld",&n,&m); if (n>m) swap(n,m); for (int i=0;i<=9;i++) f[1][i]=1ll; for (int i=2;i<=10;i++) for (int j=0;j<=9;j++) for (int k=0;k<=9;k++) if (abs(k-j)>=2) f[i][j]+=f[i-1][k]; printf("%lld\n",fun(m+1)-fun(n)); return 0; }

 

posted @ 2020-08-11 11:01  摸鱼选手LLF  阅读(151)  评论(0编辑  收藏  举报