http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5235
给定一个序列 A1,A2,A3...An 若干组询问,每组询问给出一个[L,R]
定义一个函数F(x)
- Fi(Li) = ALi
- Fi(Li + 1) = A(Li + 1)
- for all x >= Li + 2,Fi(x) = Fi(x - 1) + Fi(x - 2) ×Ax
每组询问求F(R)
对于每次递推,相当于{ { 1 , Ax } , { 1, 0 } } * { F(x-1) ,F(x-2) }
2×2的矩阵乘以2×1的矩阵得出2×1的矩阵{ F(x-1)+F(x-2)*Ax , F(x-1) }
只要求出矩阵连乘的结果,就可以得出结果
维护一棵线段树,叶子节点是{ { 1 , Ax } , { 1, 0 } },父节点是右儿子乘以左儿子,查询的时候和线段树一样
矩阵乘法不满足交换律,一定不能写反
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define l i<<1
#define r i<<1|1
using namespace std;
typedef long long ll;
ll M=1e9+7;
const int N=100009;
int a[N];
struct mat{
ll a[2][2];
mat(){
memset(a,0,sizeof(a));
}
void set(int x){
a[0][0]=a[1][0]=1;
a[0][1]=x;
a[1][1]=0;
}
mat operator*(const mat&t)const{
mat ans;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
for(int k=0;k<2;k++)
ans.a[i][j]=(ans.a[i][j]+a[i][k]*t.a[k][j])%M;
return ans;
}
}sum[N<<2];
void build(int L,int R,int i){
if(L==R){
sum[i].set(a[L]);
return ;
}
int mid=L+R>>1;
build(L,mid,l);
build(mid+1,R,r);
sum[i]=sum[r]*sum[l];//这里不能写反,矩阵乘法不满足交换律
}
mat query(int L,int R,int i,int x,int y){
if(x<=L&&R<=y)
return sum[i];
int mid=L+R>>1;
if(x>mid)
return query(mid+1,R,r,x,y);
else if(y<=mid)
return query(L,mid,l,x,y);
else
return query(mid+1,R,r,x,y)*query(L,mid,l,x,y);//这里不能写反,矩阵乘法不满足交换律
}
int main(){
int i,j,k,n,m,x,y,T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
scanf("%d",a+i);
build(1,n,1);
while(m--){
scanf("%d%d",&x,&y);
if(y<=x+1)
printf("%d\n",a[y]);
else{
mat t=query(1,n,1,x+2,y);
printf("%lld\n",(t.a[0][0]*a[x+1]%M+t.a[0][1]*a[x]%M)%M);
}
}
}
return 0;
}