北化排位赛(二)L超级Fibonacci[简中]
BUC2L - 超级 Fibonacci
题意
两个字符串按照 $$\rm Fibonacci$$ 的规则运算,求字符串的指定位。
思路
举个栗子,a="abcd", b="efg"
,可计算出:
1 | s[1] = "abcd"; |
首先弄清这个「广义 $$\rm Fibonacci$$」 的每一项的长度。显然,它亦满足 $$\rm Fibonacci$$ 的运算规则,即 $$F_n=F_{n-1}+F_{n-2}$$,上面的栗子中,
1 | len(s[1]) = 4; |
注意到,除了第 1
项外,每一项的前面所有字母都是相同的,只需计算 无穷长的数列 的第 $$y$$ 项 $$\varphi(y)$$ 即可(题给 $$x$$ 几乎是无效数据,它只用於判断 $$x=1$$ 时的特例,下文详述)
而且,数列的 $$\pmb{F_{n-1}\sim F_{n}}$$ 项和第 $$\pmb{1\sim F_{n-2}}$$ 项完全相同。
基於此,我们希望逐步缩小 $$y$$,化归为最基本的情形,如 $$\varphi(16)=\varphi(6)=\varphi(3)=c$$。
(最基本的情形即是两个所给字符串,$$\varphi(1)=e,\varphi(2)=f,\varphi(3)=g,\varphi(4)=a,\varphi(5)=b,\varphi(6)=c,\varphi(7)=d$$)
一般地,对於每一个 $$y$$,一定存在 $$n$$ 满足 $$F_{n-1}<y\leqslant F_{n}$$,即 $$F_{n-1}<y\leqslant F_{n-1}+F_{n-2}$$,也可写为 $$0<y-F_{n-1}\leqslant F_{n-2}$$,且 $$\varphi(y)=\varphi(y-F_{n-1})$$,这样就成功缩小了 $$y$$ 的上界。重复这个过程,最终便能得到答案。
实现
首先计算长度:
1 | int cnt = 2; // 广义Fibonacci的长度 |
因为不知道要算多少项,但是只需知道字符串的前 1e18
位便足够,我们用 f[i] < INF = 2e18
,加以约束。
对於每次询问,首先搜索它在哪个区间内,即搜索满足 $$F_{p-1}<y\leqslant F_{p}$$ 的 $$p$$。而刚纔已经算过 $$F_i$$ 了,二分查找即可。
然后模拟化归的过程:
1 | ll p = std::lower_bound(f, f + cnt, y) - f; |
其中,if (y <= f[p]) --p;
用来找到每次要减去的 $$F_{n-1}$$,两个约束 p>0 && y>f[0]+f[1]
防止减为负数(我最初只写 y>f[0]+f[1]
一直 $$\rm RE$$。。。
现在就化为基本情形了,也就是上文 s[3]
的那几项,显然前面是 s[2]
,后面是 s[1]
,用三目
1 | printf("%c\n", y > f[1] ? s1[y-f[1]-1] : s2[y-1]); |
愉快输出就好咯!(注意字符串的下标从 0
开始,查询的下标从 1
开始,需要 -1
别忘记 $$x=1$$ 的特殊情况,它就是 s[1]
:
1 | if (x == 1) { |
代码
1 |
|
评注
差 $$\mathcal{10}$$ 分鐘就能 這個題了、、、以爲比賽五點結束所以寫完 K 和 F 後睡了一覺才開始寫 L、、、、