zoukankan      html  css  js  c++  java
  • 4.17 斐波那契数列 K维斐波那契数列 矩阵乘法 构造

    avatar
    avatar

    一道矩阵乘法的神题 早上的时候我开挂了 想了2h想出来了。

    关于这道题我推了很多矩阵 最终推出两个核心矩阵 发现这两个矩阵放在一起做快速幂就行了。

    当k==1时 显然的矩阵乘法 多开一个位置维护前缀和即可。当然也可以 公式法:(f_1+f_2+...+f_n=f_{n+2}-1)

    证明其 只需要数学归纳法即可。

    当k==2时 不难发现 要求出((f_1+f_2+...f_n)+(f_2+...f_{n+1})+...(f_n+...f_{2n-1}))

    把这个东西 画成图 可以发现是一个平行四边形。

    考虑对这个东西求和 我开始想了一个比较麻烦的方法容斥。

    可以发现 如果我把整个矩阵都给求出来 只需要减掉不合法方案即可(f_1)被多加了n-1次 (f_2)被多加了n-2次。

    对这种形式求和怎么做?于是 我又构造了一个矩阵 多开两个位置 一个表示普通前缀和 一个表示当前前缀和。

    发现这样就能把答案算出来了。

    以上和正解毫无关系 只不过是我考试的时候的想法。

    以下是正解:

    还是考虑k==3 发现刚才的面形成了体 不过类似于平行四边形体?但是在题目角度来说其实是正方体。

    对这个东西求和 发现很难 刚才的容斥不能做了。

    不过此时可以发现每一面 由上一面加上上一面得到。

    转回头看k==2 发现每一条线可以有上面和上上面两条线得到。

    此时 我们就可以发现一个非常特殊的条件 每个维度也是由上一个维度给推出来的。

    此时我们对于每个维度单独做 然后利用上个维度做当前维度。

    这样我们得到了一个klogn的做法。注意此时的矩阵为(4cdot 4)的。

    考虑优化。

    可以发现求完一个维度之后 我们要求下一个维度。

    这个过程还是一个重复的过程。

    我们可以不手动调整 而是考虑构造一个矩阵帮我们进行调整。

    这个矩阵也很容易构造。

    剩下的就是先求出一个维度 然后利用维度生成维度是重复的。

    矩阵快速幂来做即可。

    总之 构造出来两个矩阵 可以发现 矩阵快速幂这两个矩阵的乘积即可。

    非常巧妙。

    const int MAXN=110;
    int n,k,T,m;
    ll f[5],w[5];
    struct wy
    {
    	ll a[5][5];
    	wy(){memset(a,0,sizeof(a));}
    	wy friend operator *(wy a,wy b)
    	{
    		wy c;
    		rep(1,m,i)rep(1,m,j)rep(1,m,k)
    		c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%mod;
    		return c;
    	}
    	wy friend operator ^(wy a,int p)
    	{
    		wy c;
    		rep(1,m,i)rep(1,m,j)c.a[i][j]=a.a[i][j];
    		while(p)
    		{
    			if(p&1)c=c*a;
    			a=a*a;p=p>>1;
    		}
    		return c;
    	}
    }A,B,C;
    int main()
    {
    	freopen("fib.in","r",stdin);
    	freopen("fib.out","w",stdout);
    	get(T);
    	while(T--)
    	{
    		get(n);get(k);m=4;
    		if(n==1){puts("1");continue;}
    		A.a[1][1]=0;A.a[1][2]=1;A.a[1][3]=0;A.a[1][4]=1;
    		A.a[2][1]=1;A.a[2][2]=1;A.a[2][3]=1;A.a[2][4]=1;
    		A.a[3][1]=0;A.a[3][2]=0;A.a[3][3]=1;A.a[3][4]=0;
    		A.a[4][1]=0;A.a[4][2]=0;A.a[4][3]=0;A.a[4][4]=1;
    		B.a[3][1]=1;B.a[3][3]=1;B.a[4][2]=1;B.a[4][4]=1;
    		A=A^(n-2);C=A;
    		--k;
    		if(k)
    		{
    			A=A*B;
    			A=A^(k-1);
    			A=A*C;
    		}
    		f[1]=0;f[2]=1;f[3]=0;f[4]=1;
    		memset(w,0,sizeof(w));
    		rep(1,m,i)rep(1,m,j)w[i]=(w[i]+f[j]*A.a[j][i])%mod;
    		putl(w[m]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    顺序容器的操作
    C++顺序容器
    Java8实战系列一
    Java枚举的小知识点
    Java集合框架入门介绍(一)
    测试代码格式
    Eclipse通过jdbc连接sqlserver2008数据库的两种方式
    排序算法之插入排序
    排序算法之冒泡排序
    容器扩容之分摊时间复杂度分析
  • 原文地址:https://www.cnblogs.com/chdy/p/12720376.html
Copyright © 2011-2022 走看看