像这样的明显2^k的数据,我觉得应该直接反应出递归分治法。
对于具体如何做这个D&C,Rujia给的思路真是很赞,非常值得学习。
通过仔细的观察和分析,会发现这些气球有个特点:
上半部分的左半边和右半边,以及下半部分的左半边,都全等于前一个时刻的状态。下半部分的右半边则全部是蓝气球,不予考虑
因此,我们只需要将[A,B] 这个区间分成两部分来看:中线以上的部分和中线以下的部分。
中线以上的部分,等于前一个时刻相同区间红气球数的两倍。中线以下的部分,等于这一时刻的这个区间向上挪半个高度后,在前一个时刻相同区间上的红气球数。
这样就构成了一个递归关系,我们只需要将初始时刻设为1,就可以在O(k)的时间内计算出结果了。
Rujia给的一个很精妙的转化方法:设函数 f(k, i) 为 k 时刻,前 i 行的红气球总数。那么区间 [A,B] 的红气球数就可以表示为 f(k, B) - f(k, A)
这里的解释我觉得书上说的相当清楚啦
这样做的好处我觉得是避免了拆分区间,从而避免了中线附近边界值的讨论。
Run Time: 0.016s
#define UVa "LT8-12.12627.cpp"
char fileIn[30] = UVa, fileOut[30] = UVa;
#include<cstring>
#include<cstdio>
using namespace std;
//Global Variables. Reset upon Each Case!
int T, K, A, B;
long long c[35];
/
long long f(int k, int i) {
if(!i) return 0;
if(!k) return 1;
if(i <= 1<<(k-1)) return 2*f(k-1, i);
else return f(k-1, i-(1<<(k-1))) + 2*c[k-1];
}
int main() {
c[0] = 1L;
for(int i = 1; i < 30; i ++) c[i] = 3*c[i-1];
scanf("%d", &T);
for(int kase = 1; kase <= T; kase ++) {
scanf("%d%d%d", &K, &A, &B);
long long ans = f(K, B) - f(K, A-1);
printf("Case %d: %lld\n", kase, ans);
}
return 0;
}