1. 程式人生 > >BZOJ P1026 LOJ #10165 「SCOI2009」windy數【數位DP】

BZOJ P1026 LOJ #10165 「SCOI2009」windy數【數位DP】

f[i][j]f[i][j]表示長度為ii並且最高位為jj的windy數的個數。

遞推關係:f[i][j]=f[i1][k]abs(jk)2f[i][j]=\sum f[i-1][k]\ \ \ \ \ \ \ abs(j-k)\geq2

然後我們舉個例子來討論:我們接下來要求32467\leq 32467的windy數的個數。

首先我們用w[]w[]來儲存3246732467的位數,即:w[1]=7,w[2]=6,w[3]=4,w[4]=2,w[5]=3w[1]=7,w[2]=6,w[3]=4,w[4]=2,w[5]=3

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

表示我們統計完位數不超過44的windy數的個數,

一般地,ans+=i=1len1j=19f[i][j]ans+=\sum_{i=1}^{len-1}\sum_{j=1}^9f[i][j]

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

表示我們統計完位數為55並且最高位3\leq 3的windy數的個數,我們可以這樣統計的原因是原來的數包含了這一部分並且這一部分與①不重複。

一般地,ans+=i=1w[len]1f[len][i]ans+=\sum_{i=1}^{w[len]-1}f[len][i]

③ 接下來我們考慮剩下的一部分。

我們從第二高位開始向低位列舉。

顯然31xxx31xxx這種形式的windy數的個數與f[4][1]f[4][1]相同,於是對於第ii位,我們可以從00開始列舉到w[i]

1w[i]-1,判斷與w[i+1]w[i+1]的關係進行答案累加。

而在這中間我們並不直接列舉到w[i]w[i]是因為此時的這個範圍並不被完全包含。然後我們列舉到w[i]1w[i]-1之後,進行判斷abs(w[i+1]w[i])abs(w[i+1]-w[i])22的大小關係。如果abs(w[i+1]w[i])<2abs(w[i+1]-w[i])<2就整個累加結束,這是因為如果abs(w[i+1]w[i])<2abs(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;
}