A Short problem
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1838 Accepted Submission(s): 670
Hence they prefer problems short, too. Here is a short one:
Given n (1 <= n <= 10 18), You should solve for
where
Please process until EOF (End Of File).
0 1 2
0 1 42837
这道题是比较裸的矩阵快速幂的题。但是与一般的题不一样的就是取模的是复合函数,而且是对最外层的取模,因此内部的数字可能会非常大。g(n)=3*g(n-1)+g(n-2)这个是标准的二阶线性递推式,我们可以类比斐波那契数列的公式。令Y=g(g(x)),mod=1000000007,则所求的转换成了g(Y)%mod的结果。这里出现了取模的符号。而本题的特点就在于数字会非常大。我们也知道二阶线性递推式取模必然会出现周期性的循环。因此我们可以暴力模拟得出g(Y)%mod的Y的周期,得到Y的周期为t1=222222224,即g(g(g(x)))%mod=g(g(g(x))-k*t1)%mod,所以计算时为了缩小范围我们只需计算g(g(x))%t1的值即可。即g(g(g(x)))%mod=g(g(g(x))%t1)%mod。要计算g(g(x))%t1,这样我们再次令Z=g(x),则此时所求结果为g(Z)%t1,这是可以得到Z的周期为t2=183120,即g(g(x))%t1=g(g(x)%t2)%t1。这样所求为g(x)%t2,我们再次暴力模拟,得到x的周期为t3=240。所以最终所求结果为g(g(g(x)))%mod=g(g(g(x%t3)%t2)%t1)%mod。这样通过层层取模我们便可以减小所得的数字的规模,防止数字过大导致的溢出问题。
通过这道题的训练,我们发现该题的难点不在矩阵快速幂。因为我们很清楚的知道,当出现线性递推式时,而且在n特别大时就应该有矩阵快速幂降低复杂度的意识。而本题的复合函数是一个难点。而突破这个难点就要从取模周期上降低数字的规模。取模的周期性这些小的技巧在这种难题中我们一定要注意。往往容易被忽视的小技巧才是解决问题的关键之处。
参考代码:
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<ctime>
#include<cstdlib>
#include<iomanip>
#include<utility>
#define pb push_back
#define mp make_pair
#define CLR(x) memset(x,0,sizeof(x))
#define _CLR(x) memset(x,-1,sizeof(x))
#define REP(i,n) for(int i=0;i<n;i++)
#define Debug(x) cout<<#x<<"="<<x<<" "<<endl
#define REP(i,l,r) for(int i=l;i<=r;i++)
#define rep(i,l,r) for(int i=l;i<r;i++)
#define RREP(i,l,r) for(int i=l;i>=r;i--)
#define rrep(i,l,r) for(int i=1;i>r;i--)
#define read(x) scanf("%d",&x)
#define put(x) printf("%d\n",x)
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<11
using namespace std;
int T1=222222224,T2=183120,T3=240,mod=1000000007;
ll n;
struct mat
{
ll d[2][2];
}A,B,E;
mat multi(mat a,mat b,int mod)
{
mat ans;
rep(i,0,2)
{
rep(j,0,2)
{
ans.d[i][j]=0;
rep(k,0,2)
{
if(a.d[i][k]&&b.d[k][j])
ans.d[i][j]=(ans.d[i][j]+a.d[i][k]*b.d[k][j])%mod;
}
}
}
return ans;
}
mat quickmulti(mat a,ll n,int mod)
{
mat ans=E;
while(n)
{
if(n&1)
{
n--;
ans=multi(ans,a,mod);
}
else
{
n>>=1;
a=multi(a,a,mod);
}
}
return ans;
}
int main()
{
A.d[0][0]=3,A.d[0][1]=1,A.d[1][0]=1,A.d[1][1]=0;
B.d[0][0]=1,B.d[0][1]=0,B.d[1][0]=0,B.d[1][1]=0;
E.d[0][0]=1,E.d[0][1]=0,E.d[1][0]=0,E.d[1][1]=1;
while(~scanf("%I64d",&n))
{
n%=T3;
mat ans=quickmulti(A,n,T2);
ans=multi(ans,B,T2);
int shu=ans.d[1][0];
ans=quickmulti(A,shu,T1);
ans=multi(ans,B,T1);
shu=ans.d[1][0];
ans=quickmulti(A,shu,mod);
ans=multi(ans,B,mod);
printf("%I64d\n",ans.d[1][0]);
}
}