题解 UVA10229 【Modular Fibonacci】

来自我的luogu blog

这道题目的\(n≤2^{31}\),\(m<20\),这意味着最大模数只有\(2^{20}\)

我们可以用矩阵加速来做这道题。

此题与P1962相似。但为什么那题是黄的??

详细过程可见\(OIwiki\)上的矩阵加速部分。

对了,顺带一提,用\(while\)读入多组数据时记得用\(cin\)不要问我怎么知道…

以下是代码:

#include<bits/stdc++.h>
int n,m,mod;
using namespace std;
void sp(int f[2],int a[2][2])
{
	int tmp[2];
	memset(tmp,0,sizeof(tmp));
	for(int i=0;i<2;i++) for(int j=0;j<2;j++) tmp[i]=(tmp[i]+(long long)f[j]*a[j][i])%mod;
	memcpy(f,tmp,sizeof(tmp));
}
void spup(int a[2][2])
{
	int tmp[2][2];
	memset(tmp,0,sizeof(tmp));
	for(int i=0;i<2;i++) for(int j=0;j<2;j++) for(int k=0;k<2;k++) tmp[i][j]=(tmp[i][j]+(long long)a[i][k]*a[k][j])%mod;
	memcpy(a,tmp,sizeof(tmp));
}
int main()
{
	while(cin>>n>>m)
	{
		mod=1<<m;//效率比下面的循环计算高
		//for(int i=1;i<=m;i++) mod*=2;
		int f[2]={0,1},a[2][2]={{0,1},{1,1}};
		while(n)
		{
			if(n&1) sp(f,a);
			spup(a);
			n>>=1;
		}
		printf("%d\n",f[0]);
	}
	return 0;
}

Update:2020-10-27

这两天在看如何优化取模运算,于是就发现了一条神奇的性质:

\[x\bmod2^n=x\And(2^n-1) \]

以下是证明:

\(\because (2^n-1)\)的二进制的低\(n\)位全是\(1\),其余位全是\(0\)

\(\therefore(x\And(2^n-1))\)\(x\)的二进制的低\(n\)位的值,

假设\(r=(x\And(2^n-1))\),则\(0\le r<2^n\)

\(\because (x-r)\)的二进制的低\(n\)位全是\(0\)

\(\therefore(x - r)\)\(2^n\)的整数倍,

即 存在整数\(a\),使得\(x-r=a*2^n\),即\(x=a*2^n+r\)

\(\because0\le r<2^n\)

\(\therefore\)根据余数的定义可知,\(r\)\(x\)除以\(2^n\)的余数。

\(\therefore x\bmod2^n=r=x\And(2^n-1)\)

——摘自取模运算的特殊优化

以下是新代码:

#include<bits/stdc++.h>
int n,m,mod;
using namespace std;
void sp(int f[2],int a[2][2])
{
	int tmp[2];
	memset(tmp,0,sizeof(tmp));
	for(int i=0;i<2;i++) for(int j=0;j<2;j++) tmp[i]=(tmp[i]+(long long)f[j]*a[j][i])&(mod-1);//这里
	memcpy(f,tmp,sizeof(tmp));
}
void spup(int a[2][2])
{
	int tmp[2][2];
	memset(tmp,0,sizeof(tmp));
	for(int i=0;i<2;i++) for(int j=0;j<2;j++) for(int k=0;k<2;k++) tmp[i][j]=(tmp[i][j]+(long long)a[i][k]*a[k][j])&(mod-1);//还有这里
	memcpy(a,tmp,sizeof(tmp));
}
int main()
{
	while(cin>>n>>m)
	{
		mod=1<<m;
		//for(int i=1;i<=m;i++) mod*=2;
		int f[2]={0,1},a[2][2]={{0,1},{1,1}};
		while(n)
		{
			if(n&1) sp(f,a);
			spup(a);
			n>>=1;
		}
		printf("%d\n",f[0]);
	}
	return 0;
}
posted @ 2020-11-28 20:37  子漫  阅读(90)  评论(0编辑  收藏  举报