Loading

【题解】[BJOI2012]最多的方案

数位 DP。

定理:我们可以将任意一个数拆分成若干个不相邻的斐波那契数之和。

简单证明一下:

我们从小到大枚举 \(f_i\),如果 \(n\ge f_i\) 就将 \(f_i\) 拆分出来。因为 \(f_i\le n <f_{i+1} = f_{i} + f_{i-1}\),所以拆分后一定不会选 \(f_{i-1}\),归纳一下即可证明。

对于这道题我们先将 \(n\) 拆分成若干个斐波那契数,然后写成 \(0/1\) 串的形式,每次我们可以进行操作 \(100\to 011\)。原串我们记作 \(s\)

那么我们直接从高位到低位 DP,记 \(f_{i,op},op\in\{0,1\}\) 表示考虑到第 \(i\) 位,是否有 \(>i\) 位的位置对第 \(i\) 位产生贡献。

接下来思路就很清晰了,如果 \(op + s_i = 0\),那么 \(f_{i,0} = f_{i - 1, 0}\),如果 \(op + s_i = 1\),则 \(f_{i,1} = f_{i-1,0} + f_{i-2,1}\),最后一种情况 \(op+s_i= 2\),此时 \(f_{i,1} = f_{i-2,1}\)

时间复杂度 \(\mathcal{O}(\log n)\),看了一圈发现这个 DP 比题解区的更简单,所以写一篇题解。

#define N 150
LL n, f[N], p[N][2]; int m, u[N];
LL calc(int x,int op){
	if(~p[x][op])return p[x][op];
	if(u[x] && op){
		if(x <= 2 || u[x - 1])return p[x][op] = 0;
		return p[x][op] = calc(x - 2, 1);
	}
	if(x == 1)return p[x][op] = 1;
	if(!u[x] && !op)return p[x][op] = calc(x - 1, 0);
	p[x][op] = calc(x - 1, 0);
	if(x > 2 && !u[x - 1])p[x][op] += calc(x - 2, 1);
	return p[x][op];
}
int main() {
	read(n);
	f[0] = f[1] = m = 1;
	while(f[m] < n)f[m + 1] = f[m] + f[m - 1], m++;
	pr(i, m){
		if(n >= f[i])u[i] = 1, n -= f[i];
	}
	memset(p, ~0, sizeof(p));
	printf("%lld\n", calc(m, 0));
	return 0;
}
posted @ 2022-08-04 16:34  7KByte  阅读(99)  评论(0编辑  收藏  举报