题解:[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\) 的数,就需要最高位来判断大小,统计答案。

转移是显然的,枚举数位即可:

\[f_{i,j}=\sum_{k=0}^9[\vert j-k\vert\geq2]f_{i-1,k} \]

之后考虑统计答案。记 \(n\) 的数位从高到低分别为 \(r_1,r_2,r_3,\cdots,r_L\)

数位 DP 统计答案的过程,实际上是一个不断贴近上界 \(n\) 的过程:

  1. 先考虑位数 \(<L\) 的数,显然小于 \(n\),需要统计答案 \(\displaystyle\sum_{i=1}^{L-1}\sum_{j=0}^9f_{i,j}\)

  2. 现在位数均为 \(L\),考虑最高位小于 \(r_1\) 的数,显然小于 \(n\),需要统计答案 \(\displaystyle\sum_{j=1}^{r_1-1}f_{L,j}\)。需要注意最高位不能为 \(0\)

  3. 再考虑第一位为 \(r_1\),第二位小于 \(r_2\) 的数,显然小于 \(n\),需要统计答案 \(\displaystyle\sum_{j=0}^{r_2-1}[\vert j-r_1\vert\geq2]f_{L-1,j}\)

  4. ……

  5. 直到某一位 \(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\)

  6. 最后还要统计 \(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;
}
posted @ 2026-07-25 19:06  TH911  阅读(5)  评论(0)    收藏  举报