数位dp--P2657 [SCOI2009] windy 数

预处理:$f_{i,j}$表示填了$i$位数,最高位为$j$的windy数的个数,显然有$f_{i,j}$=$\sum f_{i-1,k}$(|$j$-$k$|>=2)

1 void init(){
2   for (int i = 0;i <= 9;i++) dp[1][i]=1;
3   for (int i = 2;i <= 10;i++)
4     for (int j = 0;j <= 9;j++)
5       for (int k = 0;k <= 9;k++)
6     if (abs(j-k)>=2) dp[i][j]+=dp[i-1][k];
7 }

由于windy数的性质,可以利用前缀和,比如我要求[a,b]内windy数的个数,我们可以求出$0-b+1$和$0-a$内的windy数的个数再相减,可以把答案分成三个部分,求长度为$len$,$a$范围内的windy数的个数

(1)先求$len-1$位的windy数的个数,必定包含在区间里

1 for (int i = 1;i <= len-1;i++)
2     for (int j = 1;j <= 9;j++) ans+=dp[i][j];

(2)然后在看第$len$位,最高位<$a[len]$的windy数也一定包含在区间里

 1 for (int i = 1;i < a[len];i++) ans+=dp[len][i]; 

(3)最后看最高位相等的情况,$i$从$len-1$枚举,规定$i$之前的数都已确定,即前缀相同,如果相差>=2就加入到答案中

1  for (int i = len-1;i >= 1;i--){
2     for (int j = 0;j <= a[i]-1;j++){
3       if (abs(j-a[i+1])>=2) ans+=dp[i][j];
4     }
5     if (abs(a[i+1]-a[i])<2) break;
6   }

完整代码:

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstdio>
 4 #include <cstring>
 5 #define int long long
 6 using namespace std;
 7 int dp[15][15],a[20];
 8 void init(){
 9   for (int i = 0;i <= 9;i++) dp[1][i]=1;
10   for (int i = 2;i <= 10;i++)
11     for (int j = 0;j <= 9;j++)
12       for (int k = 0;k <= 9;k++)
13     if (abs(j-k)>=2) dp[i][j]+=dp[i-1][k];
14 }
15 int solve(int x){
16   memset(a,0,sizeof(a));
17   int len=0,ans=0;
18   while (x){
19     a[++len]=x%10;
20     x/=10;
21   }
22   for (int i = 1;i <= len-1;i++)
23     for (int j = 1;j <= 9;j++) ans+=dp[i][j];
24   for (int i = 1;i < a[len];i++) ans+=dp[len][i];
25   for (int i = len-1;i >= 1;i--){
26     for (int j = 0;j <= a[i]-1;j++){
27       if (abs(j-a[i+1])>=2) ans+=dp[i][j];
28     }
29     if (abs(a[i+1]-a[i])<2) break;
30   }
31   return ans;
32 }
33 int n,m;
34 signed main(){
35   scanf ("%lld%lld",&n,&m);
36   init();
37   cout<<solve(m+1)-solve(n)<<endl;
38   return 0;
39 }

 

 

posted @ 2020-10-09 15:42  小又又  阅读(110)  评论(0编辑  收藏  举报