把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷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
}
posted @ 2021-01-22 18:04  TheLostWeak  阅读(66)  评论(0编辑  收藏  举报