洛谷4277:萃香的请柬——题解
https://www.luogu.org/problemnew/show/P4277
萃香在小时候就一直有一个梦想,就是邀请全乡居民一起参加宴会,在上次发动异变被灵梦退治之后她仍旧没有放弃,而是在元宵节前早早准备好了难以计数的请柬。
现在,宴会即将开始,萃香却还是有一大堆请柬没有送出。经过大数学家琪露诺的严谨推算,到2018年时幻想乡的居民数目已经远远超过了外界,而这就使得宴会的邀请变得极为困难。
但是,拥有"操纵密度程度的能力"的萃香可以分成大大小小的萃香一起去送请柬。由于小萃香的移动速度过慢,因此她决定只让大萃香曲去送请柬。
开始时有S只萃香,之后每过一秒每一个大萃香会分成一大一小两个萃香,与此同时上一次分出的小萃香会积聚能量变大为大萃香。
(图片见原题)
(很容易看出,第一次的大萃香经过一秒后分成了一大一小两只萃香,之后一秒刚才分出的大萃香继续分裂,而刚才的小萃香长大为大萃香)
可是,我们这位"小小的百鬼夜行"发现了一个严重的问题:在经过无限长的时间后,萃香的数目太多了。于是她决定每一次只让一段区间内的大萃香去送请柬,而她现在想要知道每一次能够送出的请柬个数。
如果你能帮她完成这个任务,她就会送给你两个奖励——100分和宴会的请柬!
一次比赛切的题,并不知道为什么洛谷评级为提高+,不过因为早上就做了这一道题所以姑且来写个题解证明上午我没偷懒(滑稽。
这题给人感觉很像CF柯朵莉专场的A题。
首先思考给出来的字符串是没有用的,原因有两个:
1.因为时间是无限制的,所以后面所变出来的萃香永远挤不到前面,所以只和第一个字符有关。
2.因为L肯定会变成B,所以第一个字符无所谓。
综上,我们固定最开始只有一个B。
开始解题,设dp(i)表示1~i的B的个数。答案为dp(r)-dp(l-1)。(这里一定要注意,l可以为0(当时被坑了半个小时debug直到答疑帖有人问了位置……批斗一发出题人))
设f[i]为过i秒后的序列长度,打表容易发现f[i]=f[i-1]+f[i-2],这就是一个fib。(实际就是i-1的状态和i-2的状态拼接到一起成了i状态)
设g[i]为过i秒后的序列B个数,有上面不难得出g[i]=g[i-1]+g[i-2],也是个fib(注意两者的初值不一样)。
所以我们可以递归地处理dp(i,j)(多加入的j表示当前为第几秒),处理出来i最小应该属于第lev的区间,则有如下讨论:
1.i<f[lev-1],答案为dp(i,lev-1);
2.i=f[lev-1],答案为g[lev-1];
3.i>f[lev-1],答案为g[lev-1]+dp(i-f[lev-1],lev-2);
硬算即可,注意f可能爆longlong,对于第92个f特判即可。
(咦我感觉我比出题人说的简单耶)
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<ctime> #include<cstdlib> #include<map> using namespace std; typedef long long ll; const int S=1e6+3; char s[S]; ll f[101]; ll suan(int lev,ll r){ if(r<=0)return 0; if(lev==0)return 0; if(lev==1)return 1; if(r>f[lev-1])return f[lev-2]+suan(lev-2,r-f[lev-1]); else if(f[lev-1]==r)return f[lev-2]; else return suan(lev-1,r); } inline void init(){ f[0]=1;f[1]=1; for(int i=2;i<=91;i++)f[i]=f[i-1]+f[i-2]; cin>>s; } int main(){ init(); int n;ll l,r; scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%lld%lld",&l,&r); if(l>r)swap(l,r); printf("%lld\n",suan(92,r)-suan(92,l-1)); } return 0; }
+++++++++++++++++++++++++++++++++++++++++++
+本文作者:luyouqi233。 +
+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+
+++++++++++++++++++++++++++++++++++++++++++