题解 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;
}