题意:先给出一序列数字,编号从1到n,设为A[],然后再给出L和R,L代表A[L]为之后公式运算的起点,计算F(R),F(R)的公式为:如果R与L相等,则答案是A[L],如果R比L大一,则答案是A[L+1],如果大二以上,则F(R)=F(R-1)+F(R-2)*A[R]。
然后构造递推矩阵乘法:【F(R),F(R-1)】=【F(R-1),F(R-2)】*【1 , 1】
【A[R], 0】
序列长度和询问次数都是100000,序列大小1000000000,答案取模1000000007
#include <stdio.h>
#include <string.h>
#define MOD %1000000007
int a[100002];
struct node
{
int l;
int r;
long long mat[2][2];
}p[100002*4];
node mul(node c,node a,node b)//矩阵乘法
{
memset(c.mat,0,sizeof(c.mat));
for(int i=0;i<2;i++)
{
for(int j=0;j<2;j++)
{
for(int k=0;k<2;k++)
{
c.mat[i][j]=(c.mat[i][j]+a.mat[i][k]*b.mat[k][j])MOD;
}
}
}
return c;
}
void init(int l,int r,int id)//将1到n的矩阵用线段树预处理
{
p[id].l=l;
p[id].r=r;
if(l==r)
{
p[id].mat[0][0]=1;
p[id].mat[0][1]=1;
p[id].mat[1][0]=a[l];
p[id].mat[1][1]=0;
return;
}
int mid=(l+r)/2;
init(l,mid,id*2);
init(mid+1,r,id*2+1);
p[id]=mul(p[id],p[id*2],p[id*2+1]);
}
node query(int l,int r,int id)//要用到哪一段的矩阵直接拿,不用重复计算,线段树这里优化时间
{
if(p[id].l==l&&p[id].r==r)
return p[id];
int mid=(p[id].l+p[id].r)/2;
if(r<=mid)
return query(l,r,id*2);
if(l>mid)
return query(l,r,id*2+1);
return mul(p[id],query(l,mid,id*2),query(mid+1,r,id*2+1));
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",a+i);
init(1,n,1);
while(m--)
{
int l,r;
scanf("%d%d",&l,&r);
if(l==r||l+1==r)
printf("%d\n",a[r]);
else
{
node c;
c=query(l+2,r,1);
node b;
b.mat[0][0]=a[l+1];
b.mat[0][1]=a[l];
b.mat[1][0]=0;
b.mat[1][1]=0;
node ans;
ans=mul(ans,b,c);
printf("%lld\n",ans.mat[0][0]);
}
}
}
}