【洛谷2609】[ZJOI2012] 数列(高精度)
- 定义\(a_0=0,a_1=1,a_{2i}=a_i,a_{2i+1}=a_i+a_{i+1}\),求\(a_n\)。
- 数据组数\(\le20,n\le10^{100}\)
暴力的复杂度证明
考虑最朴素的倍增,发现尽管最多只有\(O(logn)\)层,但每一层可能会出现两个分支,似乎会挂。
然而,假设第一次分支得到的两个下标为\(2k,2k+1\)或\(2k-1,2k\),则下一层的转移就应该是:
- 如果是\(2k,2k+1\),有\(f_{2k}=f_k,f_{2k+1}=f_k+f_{k+1}\)。
- 如果是\(2k-1,2k\),有\(f_{2k-1}=f_{k-1}+f_k,f_{2k}=f_k\)。
容易发现,有用的下标依然只有两个!
也就是说,暴力的复杂度是正确的。
这道题中我用了一种比较简单好写的做法,也就是直接写成记忆化递归的形式,因此跑得有些慢。。。
复杂度递归一个\(log\),\(map\)一个\(log\),高精度的位数还有一个\(log\),一共三个\(log\)。
代码:\(O(Tlog^3n)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define S 2000
using namespace std;
struct Int//高精度
{
int a[S+5];I Int() {memset(a,0,sizeof(a));}
I int& operator [] (CI x) {return a[x];}I int operator [] (CI x) Con {return a[x];}
I void read()//读入
{
memset(a,0,sizeof(a));string s;cin>>s;
for(RI i=0,l=s.length();i^l;++i) a[l-i]=s[i]&15;
}
I void write() Con//输出
{
RI i=S;W(i&&!a[i]) --i;if(!i) puts("0");else {W(i) putchar(a[i--]+48);puts("");}
}
I bool operator < (Con Int& o) Con//比大小,用于map
{
for(RI i=S;i;--i) if(a[i]^o[i]) return a[i]<o[i];return 0;
}
I Int operator + (Con Int& o) Con//高精度加法
{
Int t;for(RI i=1;i<=S;++i) t[i+1]=(t[i]+=a[i]+o[i])/10,t[i]%=10;return t;
}
I Int Shr() Con//除以2(下取整)
{
Int t;for(RI i=S,w=0;i;--i) t[i]=(w=w*10+a[i])>>1,w&=1;return t;
}
}n,e;map<Int,Int> f;
I Int F(Con Int& n)//暴力递归
{
if(f.count(n)) return f[n];//记忆化
Int m=n.Shr();return f[n]=(n[1]&1?F(m)+F(m+e):F(m));//直接转移
}
int main()
{
RI Tt;e[1]=1,scanf("%d",&Tt);W(Tt--) n.read(),f.clear(),f[e]=e,F(n).write();return 0;//每组数据前需清空map,否则会MLE
}
待到再迷茫时回头望,所有脚印会发出光芒