NEFU大一暑假集训-矩阵连乘

题集链接

OP

感谢老师的讲解与付出;

感谢ph和zsl两位大佬的指导与讨论;

夹带私货:
此节课的代表性内容和例题也已经整理到自己的贴子中:【笔记】数学

A Fibonacci Numbers

题目大意

对于大于8位的斐波那契数,输出其前四位及后四位;

思路

求前四位可以使用近似公式,求后四位可以使用矩阵快速幂对1e4取模;

使用近似公式求前四位时,由于幂次太高,会爆掉doublelong double,我们可以先将其log10,再进行后续运算;

代码
#include <algorithm>
#include <cstring>
#include <iostream>
#include <math.h>
using namespace std;
typedef long long ll;
struct mtx{
	int a[2][2];
};
const int M = 10000;
const mtx sd={{{0,1},{1,1}}};
const double bas=log10((1+sqrt(5))/2);
const double fiv=log10(sqrt(5));
int f[1003],c;
mtx mul(mtx A,mtx B)
{
	mtx C;
	memset(C.a,0,sizeof(C.a));
	for(int i=0;i<2;i++)
		for(int j=0;j<2;j++)
			for(int k=0;k<2;k++)
			{
				C.a[i][j]+=A.a[i][k]*B.a[k][j];
                C.a[i][j]%=M;
			}
	return C;
}
mtx mqm(mtx A,int b)
{
	mtx ans;
	memset(ans.a,0,sizeof(ans.a));
	for(int i=0;i<2;i++)ans.a[i][i]=1;
	for(;b;b>>=1)
	{
		if(b&1)ans=mul(ans,A);
		A=mul(A,A);
	}
	return ans;
}

int main()
{
    f[2]=f[1]=1;
    for(int i=3;i;i++)
    {
        f[i]=f[i-1]+f[i-2];
        if(f[i]>=1e8){c=i;break;}
    }
    //cout<<c;
    int n;
    while(~scanf("%d",&n))
    {
        if(n<c)printf("%d\n",f[n]);
        else
        {
            double frt=bas*n-fiv;
            frt=frt-floor(frt);
            printf("%d...%04d\n",(int)(1000*pow(10,frt)),mqm(sd,n).a[1][0]);
        }
    }
    return 0;
}

B How Many Fibs?

题目大意

对于给定a和b,求有多少斐波那契数在区间 [a,b] 中;

思路

由于数据量不大,我们可以先用高精度算出所有的斐波那契数,再遍历统计即可;

代码
#include <algorithm>
#include <cstring>
#include <iostream>
#include <math.h>
#include <map>
using namespace std;
typedef long long ll;
string fbn[482],bod(102,'0'),g1,g2,l,r;
//map<string,int>mp;
int main()
{
    for(int i=0;i<=480;i++)fbn[i]=bod;
    fbn[0][101]=fbn[1][101]='1';
    //mp[fbn[0]]=0;
    for(int i=2;i<=480;i++)
    {
        int jin=0;
        for(int j=101;j>=0;j--)
        {
            int wei=fbn[i-1][j]-'0'+fbn[i-2][j]-'0'+jin;
            fbn[i][j]='0'+wei%10;
            jin=wei/10;
        }
        //mp[fbn[i]]=i;
    }
    //cout<<fbn[480];
    while(cin>>g1>>g2)
    {
        if(g1=="0"&&g2=="0")break;
        l.clear();
        r.clear();
        for(int i=1;i<=102-g1.length();i++)l+='0';
        for(int i=1;i<=102-g2.length();i++)r+='0';
        l+=g1;
        r+=g2;
        /* l[l.length()]=0;
        r[r.length()]=0; */
        //int nl=mp[lower_bound(fbn[0],fbn[481],l)],nr=mp[upper_bound(fbn[0],fbn[481],r)];
        //printf("%d\n",nr-nl);
        //cout<<l<<endl<<r<<endl;
        int ans=0;
        for(int i=1;i<=480;i++)
        {
            if(fbn[i]>=l&&fbn[i]<=r)ans++;//,cout<<fbn[i]<<endl;
        }
        printf("%d\n",ans);
    }
    return 0;
}
//0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

C Fibonacci Again

题目大意

定义了一个新的斐波那契数,求给定 f(i) 是否可以被3整除;

思路

找规律即可

代码
#include <algorithm>
#include <cstring>
#include <iostream>
#include <math.h>
#include <map>
using namespace std;
typedef long long ll;

int main()
{
    int n;
    while(cin>>n)
    {
        if(n%4==2)printf("yes\n");
        else printf("no\n");
    }
    return 0;
}

D Hat’s Fibonacci

题目大意

定义了一个新的斐波那契数列,输出给定项的值;

思路

高精度即可;

代码
#include <algorithm>
#include <cstring>
#include <iostream>
#include <math.h>
#include <map>
using namespace std;
typedef long long ll;
string fbn[7503],bod(2007,'0'),g1,g2,l,r;
//map<string,int>mp;
int main()
{
    for(int i=0;i<=7500;i++)fbn[i]=bod;
    fbn[1][2006]=fbn[2][2006]=fbn[3][2006]=fbn[4][2006]='1';
    //mp[fbn[0]]=0;
    for(int i=5;i<=7500;i++)
    {
        int jin=0;
        for(int j=2006;j>=0;j--)
        {
            int wei=fbn[i-1][j]-'0'+fbn[i-2][j]-'0'+fbn[i-3][j]-'0'+fbn[i-4][j]-'0'+jin;
            fbn[i][j]='0'+wei%10;
            jin=wei/10;
        }
    }
    int n,i;
    while (~scanf("%d",&n))
    {
        for(i=0;1;i++)if(fbn[n][i]!='0')break;
        for(i;i<=2006;i++)printf("%c",fbn[n][i]);
        printf("\n");
    }
    
    //cout<<fbn[7500];
    return 0;
}

E Fibonacci

题目大意

对于给定项的斐波那契数,输出其前四位;

代码
#include <algorithm>
#include <cstring>
#include <iostream>
#include <math.h>
using namespace std;
typedef long long ll;

const int M = 10000;

const double bas=log10((1+sqrt(5))/2);
const double fiv=log10(sqrt(5));
int f[1003],c;

int main()
{
    f[2]=f[1]=1;
    for(int i=3;i;i++)
    {
        f[i]=f[i-1]+f[i-2];
        if(f[i]>=1e4){c=i;break;}
    }
    //cout<<c;
    int n;
    while(~scanf("%d",&n))
    {
        if(n<c)printf("%d\n",f[n]);
        else
        {
            double frt=bas*n-fiv;
            frt=frt-floor(frt);
            printf("%d\n",(int)(1000*pow(10,frt)));
        }
    }
    return 0;
}

F Another kind of Fibonacci

题目大意

给定 n ,输出 ∑ i = 0 n f ( i ) 2 \sum_{i=0}^nf(i)^2 i=0nf(i)2

代码

思路详见【笔记】数学中的例题

#include <algorithm>
#include <cstring>
#include <iostream>
#include <math.h>
using namespace std;
typedef long long ll;
struct mtx{
	int a[4][4];
};
const ll M = 10007;
mtx mul(mtx A,mtx B)
{
	mtx C;
	memset(C.a,0,sizeof(C.a));
	for(int i=0;i<4;i++)
		for(int j=0;j<4;j++)
			for(int k=0;k<4;k++)
			{
				C.a[i][j]+=A.a[i][k]*B.a[k][j];
                C.a[i][j]%=M;
			}
	return C;
}
mtx mqm(mtx A,int b)
{
	mtx ans;
	memset(ans.a,0,sizeof(ans.a));
	for(int i=0;i<2;i++)ans.a[i][i]=1;
	for(;b;b>>=1)
	{
		if(b&1)ans=mul(ans,A);
		A=mul(A,A);
	}
	return ans;
}

int main()
{
    ll n,x,y;
    while(~scanf("%lld%lld%lld",&n,&x,&y))
    {
        mtx bas={{{1,x*x%M,y*y%M,2*x*y%M},{0,x*x%M,y*y%M,2*x*y%M},{0,1,0,0},{0,x%M,0,y%M}}};
        bas=mqm(bas,n-1);
        printf("%lld\n",(2*bas.a[0][0]+bas.a[0][1]+bas.a[0][2]+bas.a[0][3])%M);
    }
    return 0;
}

G Fibonacci Subsequence

题目大意

给定一个数列,求其中符合斐波那契规律的(不要求连续)子数列的最长长度,并输出数列;

思路

dp,遍历所有的元素对作为前两项,在哈希表中判断是否存在第三项,并以此进行状态转移;

详见代码~

代码
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <map>

using namespace std;

#define ll long long
#define N 3010
int a[N];
short dp[N][N]; 
int n;
map<int,int>mp;
map<int,int>::iterator it;
 
int main()
{ 
    int test=0;
    while(scanf("%d",&n)!=EOF)
    {
		if(test!=0) printf("\n");
		test++;
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
			dp[i][j]=1;
		mp.clear();
		int m=0,x,y;
		x=a[1];
		for(int i=n;i>=0;i--)
		{
			for(int j=1;j<=i-1;j++)
			{
				int k=a[i]+a[j];
				it=mp.find(k);
				if(it!=mp.end())
				{
					dp[j][i]=dp[i][it->second]+1;
					if(dp[j][i]>m)
					{
						m=dp[j][i];
						x=j; y=i;
					}
				}
	    	}
			mp[a[i]]=i;
		}
		if(n==1)
		{
			printf("1\n%d\n",a[1]);
			continue;
		}
		if(n==2)
		{
			printf("2\n%d %d\n",a[1],a[2]);
			continue;
		}
		if(m==0)
		{
			printf("2\n");
			printf("%d %d\n",a[1],a[2]); 
			continue;}
		printf("%d\n",m+1);
		x=a[x],y=a[y];
		printf("%d",x);
		for(int i=1;i<=m;i++)
		{
			printf(" %d",y);
			int z=x+y; x=y;y=z;
		}
		printf("\n");
    }
    return 0;
}

ED

矩阵连乘专题好像变成了斐波那契专题哈;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值