BZOJ P1026 LOJ #10165 「SCOI2009」windy数【数位DP】【详细题解】

f [ i ] [ j ] f[i][j] f[i][j]表示长度为 i i i并且最高位为 j j j的windy数的个数。

递推关系: f [ i ] [ j ] = ∑ f [ i − 1 ] [ k ]         a b s ( j − k ) ≥ 2 f[i][j]=\sum f[i-1][k]\ \ \ \ \ \ \ abs(j-k)\geq2 f[i][j]=f[i1][k]       abs(jk)2

然后我们举个例子来讨论:我们接下来要求 ≤ 32467 \leq 32467 32467的windy数的个数。

首先我们用 w [ ] w[] w[]来存储 32467 32467 32467的位数,即: w [ 1 ] = 7 , w [ 2 ] = 6 , w [ 3 ] = 4 , w [ 4 ] = 2 , w [ 5 ] = 3 w[1]=7,w[2]=6,w[3]=4,w[4]=2,w[5]=3 w[1]=7,w[2]=6,w[3]=4,w[4]=2,w[5]=3

① 我们可以累加答案 a n s + = ∑ i = 1 4 ∑ j = 1 9 f [ i ] [ j ] ans+=\sum_{i=1}^{4}\sum_{j=1}^{9}f[i][j] ans+=i=14j=19f[i][j]

表示我们统计完位数不超过 4 4 4的windy数的个数,

一般地, a n s + = ∑ i = 1 l e n − 1 ∑ j = 1 9 f [ i ] [ j ] ans+=\sum_{i=1}^{len-1}\sum_{j=1}^9f[i][j] ans+=i=1len1j=19f[i][j]

② 累加答案 a n s + = ∑ i = 1 2 f [ 5 ] [ i ] ans+=\sum_{i=1}^{2}f[5][i] ans+=i=12f[5][i]

表示我们统计完位数为 5 5 5并且最高位 ≤ 2 \leq 2 2的windy数的个数,我们可以这样统计的原因是原来的数包含了这一部分并且这一部分与①不重复。

一般地, a n s + = ∑ i = 1 w [ l e n ] − 1 f [ l e n ] [ i ] ans+=\sum_{i=1}^{w[len]-1}f[len][i] ans+=i=1w[len]1f[len][i]

③ 接下来我们考虑剩下的一部分。

我们从第二高位开始向低位枚举。

显然 31 x x x 31xxx 31xxx这种形式的windy数的个数与 f [ 4 ] [ 1 ] f[4][1] f[4][1]相同,于是对于第 i i i位,我们可以从 0 0 0开始枚举到 w [ i ] − 1 w[i]-1 w[i]1,判断与 w [ i + 1 ] w[i+1] w[i+1]的关系进行答案累加。

而在这中间我们并不直接枚举到 w [ i ] w[i] w[i]是因为此时的这个范围并不被完全包含。然后我们枚举到 w [ i ] − 1 w[i]-1 w[i]1之后,进行判断 a b s ( w [ i + 1 ] − w [ i ] ) abs(w[i+1]-w[i]) abs(w[i+1]w[i]) 2 2 2的大小关系。如果 a b s ( w [ i + 1 ] − w [ i ] ) &lt; 2 abs(w[i+1]-w[i])&lt;2 abs(w[i+1]w[i])<2就整个累加结束,这是因为如果 a b s ( w [ i + 1 ] − w [ i ] ) &lt; 2 abs(w[i+1]-w[i])&lt;2 abs(w[i+1]w[i])<2那么之后的所有数都会包含这个也就不符合条件了。

④ 累加完之后考虑一下这个数本身是否被考虑进去了。

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define rep(i,x,y) for(ll i=(x);i<=(y);i++)
#define repl(i,x,y) for(ll i=(x);i<(y);i++)
#define repd(i,x,y) for(ll i=(x);i>=(y);i--)
using namespace std;

const ll N=25;
const ll Inf=1e18;

ll l,r,w[N],f[N][N];

inline ll read() {
	ll x=0;char ch=getchar();bool f=0;
	while(ch>'9'||ch<'0'){if(ch=='-')f=1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return f?-x:x;
}

void split(ll x) {
	w[0]=0;
	while(x) {
		w[++w[0]]=x%10;x/=10;
	}
}

ll calc(ll x) {
	ll ans=0;
	
	split(x);
	
	rep(i,1,w[0]-1) rep(j,1,9) ans+=f[i][j];
	
	rep(i,1,w[w[0]]-1) ans+=f[w[0]][i];
	
	repd(i,w[0]-1,1) {
		rep(j,0,w[i]-1) if(abs(j-w[i+1])>=2) ans+=f[i][j];
		
		if(abs(w[i+1]-w[i])<2) break;
		
		if(i==1) ans++;
	}
	
	return ans;
}

int main() {
	rep(i,0,9) f[1][i]=1;
	
	rep(i,2,N-1) rep(j,0,9) rep(k,0,9) if(abs(j-k)>=2) f[i][j]+=f[i-1][k];
	
	l=read(),r=read();
	
	printf("%lld\n",calc(r)-calc(l-1));

	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值