数位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 }