BZOJ 1026 [SCOI2009] windy数

来源:互联网 发布:股票行情交易软件 编辑:程序博客网 时间:2024/05/05 08:43

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 。

Source

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

数位DP~

用f[i][j]表示第i位上填j时的方案数,那么f[i][j]=sum{f[i-1][k]},其中abs(j,k)>=2。

要得到[a,b]的答案,我们可以用ans[1,b]-ans[1,a-1],所以要计算的就是[1,u]的答案。

我们可以把它分为两个部分(设数u的第i位为x[i],总位数为tot):

(1)计算首位不为x[tot]的种类数,枚举位数,首位贡献的答案是f[tot][1...x[tot]-1],其余每位贡献的答案是f[i][1...9];

(2)计算首位为x[tot]的种类数,从高往低枚举,先计算完第i位<x[i]的种类数,再以第i位是x[i]为基准往下计算。这里注意,如果abs(x[i],x[i+1])<2,就不用再往下计算。

但是用上面的方法计算出来的是1~u-1的种类数,所以我们在输出的时候要输出ans[1,u-1]。

答案一定小于2*10^9,int就够了。


#include<cstdio>int a,b,f[12][10];int abs(int u){return u>0 ? u:-u;}int cal(int u){if(!u) return 0;int x[12],tot=0,ans=0;while(u) x[++tot]=u%10,u/=10;for(int i=1;i<x[tot];i++) ans+=f[tot][i];for(int i=tot-1;i;i--)  for(int j=1;j<=9;j++) ans+=f[i][j];for(int i=tot-1;i;i--){for(int j=0;j<x[i];j++)  if(abs(x[i+1]-j)>=2) ans+=f[i][j];if(abs(x[i+1]-x[i])<2) break;}return ans;}int main(){scanf("%d%d",&a,&b);for(int i=0;i<=9;i++) f[1][i]=1;for(int i=1;i<=11;i++)  for(int j=0;j<=9;j++)    for(int k=0;k<=9;k++)      if(k>j+1 || j>k+1) f[i][j]+=f[i-1][k];printf("%d\n",cal(b+1)-cal(a));return 0;}


1 0
原创粉丝点击