Codeforces 896A Nephren gives a riddle(模拟递归)
题意
一个初始字符串f0f_0f0
What are you doing at the end of the world? Are you busy? Will you save us?
然后开始递归复读
What are you doing while sending “What are you doing at the end of the world? Are you busy? Will you save us?”? Are you busy? Will you send “What are you doing at the end of the world? Are you busy? Will you save us?”?
What are you doing while sending “What are you doing while sending “What are you doing at the end of the world? Are you busy? Will you save us?”? Are you busy? Will you send “What are you doing at the end of the world? Are you busy? Will you save us?”?”?Are you busy? Will you send “What are you doing while sending “What are you doing at the end of the world? Are you busy? Will you save us?”? Are you busy? Will you send “What are you doing at the end of the world? Are you busy? Will you save us?”?”?
…
/*难怪极度痛恨复读机的qls的群名片要改成Nephren*/
给q个查询,每个查询有两个数,n和k,表示询问第n个字符串的第k位是什么(n是从0开始的,k从1开始),如果k大于该字符串的长度,输出句号。
思路
我先打个表,一开始用string去打,100不到就溢出string的最大范围了(第一次爆了string的长度),于是我找到规律就是一个递推式:
∣fi∣|f_i|∣fi∣=2×∣fi−1∣+682\times |f_{i-1}| +682×∣fi−1∣+68
k的范围限定在1e18,然后发现表打到第53个的时候,数据(字符串长度)就超过1e18了,这里肯定有名堂,先放着。
我们观察一下这个字符串的结构,发现它可以拆成这么几个三个部分:
part1part_1part1:What are you doing while sending "
part2part_2part2:"? Are you busy? Will you send "
part3part_3part3:"?
fif_ifi=part1part_1part1+fi−1f_{i-1}fi−1+part2part_2part2+fi−1f_{i-1}fi−1+part3part_3part3
那么我们就可以选择递归一下(其实有点像倒着做DP的感觉,也有点像DFS搜索)
- 判断如果k小于part1part_1part1的长度,直接取part1(k−1)part_{1(k-1)}part1(k−1)
- 如果k介于part1part_1part1和part2part_2part2之间,递归下一层,把k减去part1part_1part1的长度,去fi−1f_{i-1}fi−1找
- 如果k在part2part_2part2长度范围内,那么就取part2(k−part1−∣fi−1)∣part_{2 (k-part_1-|f_{i-1})|}part2(k−part1−∣fi−1)∣
- 如果k介于part2part_2part2和part3part_3part3之间,递归下一层,把k减去part1+part2+∣fi−1∣part_1+part_2+|f_i-1|part1+part2+∣fi−1∣的长度,去fi−1f_{i-1}fi−1找
- 如果k在part3part_3part3长度范围内,那么就取part3(k−(part1+part2+2×∣fi−1∣))part_{3 (k-(part_1+part_2+2\times|f_i-1|))}part3(k−(part1+part2+2×∣fi−1∣))
- 如果k范围溢出当前fif_ifi的长度,就取句号’.’
因为刚刚打过表发现大于53的时候,长度就溢出1e18了,所以此时的判断只可能发生在 1 和 2之间,我们就不需要打后面的表以求出fif_ifi 的长度了。
整体的时间复杂度为O(n)O(n)O(n)
代码
#include <iostream>
using namespace std;
string a[100];
unsigned long long flen[100];
string f0="What are you doing at the end of the world? Are you busy? Will you save us?";
string part1="What are you doing while sending \"";
string part2="\"? Are you busy? Will you send \"";
string part3="\"?";
unsigned long long limi=0;
char fucker(unsigned long long nn,unsigned long long kk)
{
if(nn==0)
{
if(kk>f0.size())
{
return '.';
} else{
return f0[kk-1];
}
}
else if(nn>limi)
{
if(kk>part1.size())
{
return fucker(nn-1,kk-part1.size());
}
else
{
return part1[kk-1];
}
}
else{
if(kk<=part1.size())
{
return part1[kk-1];
}
else if(kk<=(part1.size()+flen[nn-1]))
{
return fucker(nn-1,kk-part1.size());
}
else if(kk<=(part1.size()+flen[nn-1]+part2.size()))
{
return part2[kk-(part1.size()+flen[nn-1])-1];
}
else if(kk<=(part1.size()+2*flen[nn-1]+part2.size()))
{
return fucker(nn-1,kk-part1.size()-flen[nn-1]-part2.size());
}
else if(kk<=flen[nn])
{
return part3[kk-part1.size()-2*flen[nn-1]-part2.size()-1];
}
else
{
return '.';
}
}
}
int main() {
flen[0]=75;
flen[1]=68+2*flen[0];
for(unsigned long long i=2;i<100;i++)
{
flen[i]=2*flen[i-1]+68;
if(flen[i]>1e18)
{
limi=i;
break;
}
}
unsigned long long q;
cin>>q;
for(unsigned long long i=0;i<q;i++)
{
unsigned long long n;
cin>>n;
unsigned long long k;
cin>>k;
cout<<fucker(n,k);
}
cout<<endl;
return 0;
}
需要注意以下数字范围问题(无脑换unsigned long long就对了)还有k是从0开始计。
今天是自己想到了的,好开心,没有参考~