BZOJ3286 Fibonacci矩阵 矩阵 快速幂 卡常

欢迎访问~原文出处——博客园-zhouzhendong

去博客园看该题解


题目传送门 - BZOJ3286


题意概括

n,m,a,b,c,d,e,f<=10^1000000


 

题解

  神奇的卡常题目。

  在此感谢"zhouzixuan"——bzoj 3286: Fibonacci矩阵

  学习他,才15秒卡过此题。

  这题的做法应该很明显的,学过矩阵快速幂的大概几眼就看出来了。

  对于每一行的转移,是相同的,所以矩阵快速幂可以搞定行与行之间的转移。

  然后对于某一行,其实大部分的转移是和abc有关的,同理也可用矩阵快速幂搞定。

  但是如果折半的话,矩阵快速幂的复杂度为log2(n)*len(n)   (高精度运算复杂度)

  要TLE。

  然后我们发现,如果10位10位走,就不用跑高精度了。那么时间复杂度理论上就可以过去了。

  但是这题神坑。

  卡常!!!!!!!

  那么,我们研究矩阵,发现,我们构造的矩阵,有一列(或者一行)的3个数的值总是不变的。

  那么,我们可以减少循环??

  然后,去掉循环,手写9个运算式又可以省去循环变量的几个运算的复杂度。

  然后,仍然不够。我们发现数组访问有点慢,那么我们把数组去掉,改成一个一个的变量。

  这样大概可以快上4倍。

  然后,我们发现之前的那3个不变的也没用了,直接不运算了,其余的按照原先的运算,其中涉及之前的那3个的就把变量名改成值就可以了。

  然后发现还是TLE了。

  您千万不要气馁。

  对于矩阵快速幂中,我原先的版本是这样的:

Mat MatPow(Mat x,int *v,int len){
    Mat ans(1),xx;
    for (int i=1;i<=len;i++){
        for (int j=v[i];j--;)
            ans=ans*x;
        xx=x=x*x,x=x*x,x=x*x,x=x*xx;
    }
    return ans;
}

  其实,这个时候,我们不仅要在x上面花时间,又要在ans上花时间,所以,我们要想办法卡。

  事实上,我们可以这样:(省掉了约1/3的时间)

  

inline Mat MatPow(Mat x,int *v,int len){
    Mat ans(1),xx;
    Mat fac[10];
    fac[0].set(1);
    for (int i=1;i<=9;i++)
        fac[i]=fac[i-1]*x;
    for (int i=len;i>=1;i--)
        ans=MatPow(ans)*fac[v[i]];
    return ans;
}

  

  然后我就过去了。(心)累死了。

 


update 2017-12-22

在写BZOJ3240的时候,突然发现我当初在写最后的MatPow的时候,有重复计算。然后舍去了重复计算,卡掉了1/3的常数,现在是10秒了。


 

代码

#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cmath>
using namespace std;
typedef long long LL;
const int mod=2012182013;
struct BigInt{
	static const int MaxL=1000005;
	int v[MaxL],len;
	inline bool isd(char ch){return '0'<=ch&&ch<='9';}
	inline void read(){
		len=0;
		char ch=getchar();
		while (!isd(ch))
			ch=getchar();
		while (isd(ch))
			v[++len]=ch-48,ch=getchar();
		for (int i=1;i<=len/2;i++)
			swap(v[i],v[len+1-i]);
	}
	inline void minus(){
		v[1]--;
		for (int i=1;i<len;i++)
			if (v[i]<0)
				v[i]+=10,v[i+1]--;
			else
				break;
		while (len&&!v[len])
			len--;
	}
};
inline bool isd(char ch){return '0'<=ch&&ch<='9';}
inline LL getMODed(){
	char ch=getchar();
	while (!isd(ch))
		ch=getchar();
	LL res=0;
	while (isd(ch))
		res=(res*10+ch-48)%mod,ch=getchar();
	return res;
}
struct Mat{
	LL v11,v12,v21,v22,v31,v32;
	inline Mat (){}
	inline Mat (int x){(*this).set(x);}
	inline void set(int x){
		v11=v12=v21=v22=v31=v32=0;
		if (x==1)
			v11=v22=1;
	}
	inline void build(int a,int b,int c){
		v11=0,v12=a;
		v21=1,v22=b;
		v31=0,v32=c;
	}
	inline Mat operator * (Mat &b){
		Mat ans;
		ans.v11=(v11*b.v11+v12*b.v21)%mod;
		ans.v12=(v11*b.v12+v12*b.v22)%mod;
		ans.v21=(v21*b.v11+v22*b.v21)%mod;
		ans.v22=(v21*b.v12+v22*b.v22)%mod;
		ans.v31=(v31*b.v11+v32*b.v21+b.v31)%mod;
		ans.v32=(v31*b.v12+v32*b.v22+b.v32)%mod;
		return ans;
	}
};
inline Mat MatPow(Mat x){
	Mat ans(1);
	for (int y=10;y;y>>=1,x=x*x)
		if (y&1)
			ans=ans*x;
	return ans;
}
inline Mat MatPow(Mat x,int *v,int len){
	Mat ans(1),xx;
	Mat fac[10];
	fac[0].set(1);
	for (int i=1;i<=9;i++)
		fac[i]=fac[i-1]*x;
	for (int i=len;i>=1;i--)
		ans=MatPow(ans)*fac[v[i]];
	return ans;
}
LL a,b,c,d,e,f;
BigInt n,m;
Mat Mx,My,en,res;
int main(){
	n.read(),m.read();
	a=getMODed(),b=getMODed(),c=getMODed();
	d=getMODed(),e=getMODed(),f=getMODed();
	Mx.build(a,b,c),My.build(d,e,f);
	m.minus(),m.minus(),n.minus();
	res=MatPow(Mx,m.v,m.len);
	en=MatPow(res*My*My,n.v,n.len)*res;
	printf("%lld",(en.v12+en.v22+en.v32)%mod);
	return 0;
}

  

posted @ 2017-12-20 16:11  zzd233  阅读(692)  评论(0编辑  收藏  举报