题解:[SCOI2009] windy 数(加强版)
题意分析
原题 \(1\leq a,b\leq10^9\),可以考虑打表。但是现在开到 \(10^{18}\) 打不了了,而且对于 windy 数的限制是和数位相关的,考虑数位 DP。
考虑数位 DP,显然状态要维护数位相关信息,不然没法转移。
求 \([a,b]\) 内的 windy 数的个数,可以差分,用 \([1,b]\) 的答案减去 \([1,a-1]\) 的答案。
设 \(f_{i,j}\) 表示一个数 \(x\) 有 \(i\) 位,最高位为 \(j\),所有小于等于 \(x\) 的 windy 数的个数。关于状态设计里是最高位而不是最低位,是因为我们需要维护 \(\leq n\) 的数,就需要最高位来判断大小,统计答案。
转移是显然的,枚举数位即可:
之后考虑统计答案。记 \(n\) 的数位从高到低分别为 \(r_1,r_2,r_3,\cdots,r_L\)。
数位 DP 统计答案的过程,实际上是一个不断贴近上界 \(n\) 的过程:
-
先考虑位数 \(<L\) 的数,显然小于 \(n\),需要统计答案 \(\displaystyle\sum_{i=1}^{L-1}\sum_{j=0}^9f_{i,j}\)。
-
现在位数均为 \(L\),考虑最高位小于 \(r_1\) 的数,显然小于 \(n\),需要统计答案 \(\displaystyle\sum_{j=1}^{r_1-1}f_{L,j}\)。需要注意最高位不能为 \(0\)。
-
再考虑第一位为 \(r_1\),第二位小于 \(r_2\) 的数,显然小于 \(n\),需要统计答案 \(\displaystyle\sum_{j=0}^{r_2-1}[\vert j-r_1\vert\geq2]f_{L-1,j}\)。
-
……
-
直到某一位 \(l\),使得 \(\vert r_l-r_{l-1}\vert<2\) 不符合 windy 数的限制,那么就统计答案到 \(\displaystyle\sum_{j=0}^{r_l-1}[\vert j-r_{l-1}\vert\geq2]f_{L-l+1,j}\)。
如果不存在 \(\vert r_l-r_{l-1}\vert<2\),取 \(l=L\)。
-
最后还要统计 \(n\) 自己。
AC 代码
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
typedef long long ll;
constexpr const int N=19,inf=0x3f3f3f3f;
ll a,b,f[N+1][10];
void build(){
for(int i=0;i<=9;i++){
f[1][i]=1;
}
for(int i=2;i<=N;i++){
for(int j=0;j<=9;j++){
for(int k=0;k<=9;k++){
if(abs(j-k)>=2){
f[i][j]+=f[i-1][k];
}
}
}
}
}
ll calc(ll x){
ll ans=0;
int L=floor(log10(x))+1;
for(int i=1;i<L;i++){
for(int j=1;j<=9;j++){
ans+=f[i][j];
}
}
vector<int>r;
do{
r.push_back(x%10);
x/=10;
}while(x);
r.push_back(inf);
reverse(r.begin(),r.end());
int l=1;
for(;l+1<=L;l++){
if(abs(r[l+1]-r[l])<2){
break;
}
}
if(l==L){
ans++;
}else{
l++;
}
for(int i=1;i<=l;i++){
for(int j=(i==1);j<r[i];j++){
if(abs(j-r[i-1])>=2){
ans+=f[L-i+1][j];
}
}
}
return ans;
}
int main(){
/*freopen("test.in","r",stdin);
freopen("test.out","w",stdout);*/
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>a>>b;
build();
cout<<calc(b)-calc(a-1)<<'\n';
cout.flush();
/*fclose(stdin);
fclose(stdout);*/
return 0;
}

浙公网安备 33010602011771号