【洛谷2609】[ZJOI2012] 数列(高精度)

点此看题面

  • 定义(a_0=0,a_1=1,a_{2i}=a_i,a_{2i+1}=a_i+a_{i+1}),求(a_n)
  • 数据组数(le20,nle10^{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
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu2609.html