题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5235
这道题的描述很友好,很好读,于是不解释题意了。。。
这道题的解法貌似也不少,我就说我比赛时用的做法吧。
首先是那个递推的式子,很容易让人联想到斐波那契数列的感脚,而斐波那契的求解可以用矩阵加速,那么这道题可以尝试构造个矩阵。
那么对于Fi+1 和 Fi,要计算出Fi+2的话,按照递推式有:
那么,如果我们要求对应的F(r)的话,可以通过先将中间的矩阵乘起来,直接拿区间的前两项乘上这个区间就好了。
那么问题就是,怎么获取这个矩阵?如果是求和,如果已经处理好前缀S[r],那么l到r这个区间上的和就可以用S[r]-S[l-1]得到,利用这种思想来考虑这个问题的话,
我们可以先计算出所有矩阵的前缀积S[i],这个没问题,构造一个就用前面的乘过来就行了,我们用X[i][j]来表示从i到j的矩阵的积。
对于我们的问题,如果L等于R,或是只比R小一,那么我们可以直接输出A[R]的值;
对于差值更大的,其实我们中间要乘的矩阵就是X[L+2][R],我们知道S[L+1] * X[L+2][R] = S[R],利用矩阵运算的性质,我们可以得到
X[L+2][R] = REV[L+1] * S[R],其中REV[L+1]是S[L+1]的逆,也就是说两者相乘得到一个单位矩阵。
至于求逆,每个矩阵对应的逆可以在计算出的前缀积的时候顺便计算,计算逆最直接的做法就是列方程求解(我就是这样干的),不过貌似有更高级的做法,毕竟这里2*2的矩阵手算可以,要是矩阵大了估计就呵呵了。。。
剩下的工作就是细心点慢慢码了。。。
PS:第一次写矩阵求逆,写得好搓。。。
#include<cstdio>
#include<cstring>
#define LL long long
#define mod 1000000007
inline void in(int& x){
x=0;
char c=getchar();
while(c<48 || c>57) c=getchar();
while(c>=48 && c<=57){
x = x*10+c-48;
c = getchar();
}
}
struct Matrix{
LL x[2][2];
};
int t, n, m, l, r;
int a[100001];
Matrix s[100001], re[100001], tmp;
LL ans;
Matrix mul(Matrix& A, Matrix& B){
Matrix c;
for(int i=0; i<2; i++){
for(int j=0; j<2; j++){
c.x[i][j]=0;
for(int k=0; k<2; k++){
c.x[i][j] += (A.x[i][k]*B.x[k][j])%mod;
c.x[i][j] %= mod;
}
}
}
return c;
}
void gcd(LL A, LL B, LL& d, LL& x, LL& y){
if(!B){
d=A; x=1; y=0;
}
else{
gcd(B, A%B, d, y, x);
y -= x*(A/B);
}
}
LL inv(LL A){
LL d, x, y;
gcd(A, mod, d, x, y);
return (x+mod)%mod;
}
Matrix rev(Matrix& A){
Matrix B;
LL g1 = (A.x[0][0]*A.x[1][1]%mod - A.x[0][1]*A.x[1][0]%mod)%mod;
g1 = (g1+mod)%mod;
LL ni1 = inv(g1);
LL ni2 = inv(mod-g1);
LL aa, bb, cc, dd;
cc = A.x[1][0]*ni2%mod;
aa = -1 * (A.x[1][1]*cc%mod)*inv(A.x[1][0])%mod;
aa = (aa%mod+mod)%mod;
dd = A.x[0][0]*ni1%mod;
bb = -1 * (A.x[0][1]*dd%mod)*inv(A.x[0][0])%mod;
bb = (bb%mod+mod)%mod;
B.x[0][0]=aa;
B.x[0][1]=bb;
B.x[1][0]=cc;
B.x[1][1]=dd;
return B;
}
int main(){
in(t);
while(t--){
in(n); in(m);
s[0].x[0][0]=s[0].x[1][1]=1;
s[0].x[0][1]=s[0].x[1][0]=0;
for(int i=1; i<=n; i++){
in(a[i]);
s[i].x[0][0]=s[i].x[0][1]=1;
s[i].x[1][0]=a[i];
s[i].x[1][1]=0;
s[i] = mul(s[i-1], s[i]);
re[i] = rev(s[i]);
}
while(m--){
in(l); in(r);
if(r<=l+1) printf("%d\n", a[r]);
else{
tmp = mul(re[l+1], s[r]);
LL A=a[l+1], B=a[l];
ans = (A*tmp.x[0][0])%mod + (B*tmp.x[1][0])%mod;
ans = (ans%mod+mod)%mod;
printf("%lld\n", ans);
}
}
}
return 0;
}