一、题目
二、解法
面对这种题的时候,不要急,急了反而没有。真正的方法是一步一步来,你会发现都是套路。
解决本题需要三步走,这是由中国的国情决定的
0x01
怎么计算
f
i
b
2
(
i
)
fib^2(i)
fib2(i) ,由于
i
i
i很大(
1
e
10
1e10
1e10范围),除了矩阵加速我们别无选择,紧紧抓住我们唯一拥有的条件:
f
i
b
(
i
)
=
f
i
b
(
i
−
1
)
+
f
i
b
(
i
−
2
)
fib(i)=fib(i-1)+fib(i-2)
fib(i)=fib(i−1)+fib(i−2)那么
f
i
b
2
(
i
)
=
f
i
b
2
(
i
−
1
)
+
f
i
b
2
(
i
−
2
)
+
2
f
i
b
(
i
−
1
)
f
i
b
(
i
−
2
)
fib^2(i)=fib^2(i-1)+fib^2(i-2)+2fib(i-1)fib(i-2)
fib2(i)=fib2(i−1)+fib2(i−2)+2fib(i−1)fib(i−2)那么维护三个变元:
f
i
b
2
(
i
)
,
f
i
b
2
(
i
−
1
)
,
f
i
b
(
i
)
f
i
b
(
i
−
1
)
fib^2(i),fib^2(i-1),fib(i)fib(i-1)
fib2(i),fib2(i−1),fib(i)fib(i−1),矩阵不难写出吧:
1
1
2
0
1
0
0
0
1
\begin{matrix}1&1&2\\0&1&0\\0&0&1\end{matrix}
100110201分析得到我们要的是
i
i
i次方后维护第二行第一列的值。
0x02
这个问题是如果解决子集枚举的问题,好像这个部分我见到的套路着实不多,其中有一种是算贡献(这里用不到),或者是利用一些特殊性质。
f
i
b
2
(
S
)
fib^2(S)
fib2(S)的计算其实就是属于里面元素的转移矩阵相乘,支持乘法的性质,有趣。我们加入单位矩阵来表示选与不选:
(
I
+
A
s
1
)
(
I
+
A
s
2
)
.
.
.
.
.
.
(I+A^{s_1})(I+A^{s_2})......
(I+As1)(I+As2)......其中
I
I
I表示单位矩阵,
A
A
A表示转移矩阵,那么这一部分就完成了!
0x03
他吗的,外面还套了一个区间计算,还有单点修改,很明显了,考虑下线段树。
关键在于线段树两个儿子的合并,类似于
c
d
q
cdq
cdq,单独选左边儿子和右边儿子的贡献我们已经知道了,现在要算交叉贡献:
上面画的线代表前缀 / / /后缀的乘积,交叉贡献是任取一条红线和蓝线乘起来算贡献,那么维护左边蓝线的和,右边红线的和,直接将他们相乘就可以了,我们得到了维护答案的方法。
那么红线 / / /蓝线和其实也很好维护,为了维护他们,我们需要再维护一个区间全部乘积,这里就不在赘述啦。
没骗你吧,就是套路题。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 100005;
const int MOD = 998244353;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m;
struct Matrix
{
int a[4][4];
Matrix() {memset(a,0,sizeof a);}
Matrix operator * (const Matrix &b) const
{
Matrix r;
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
for(int k=1;k<=3;k++)
r.a[i][k]=(r.a[i][k]+1ll*a[i][j]*b.a[j][k])%MOD;
return r;
}
Matrix operator + (const Matrix &b) const
{
Matrix r;
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
r.a[i][j]=(a[i][j]+b.a[i][j])%MOD;
return r;
}
void print()
{
for(int i=1;i<=3;i++,puts(""))
for(int j=1;j<=3;j++)
printf("%d ",a[i][j]);
}
}E,I,A;
struct node
{
Matrix pr,sf,ml;int ans;
node() {ans=0;}
node operator + (const node &b) const
{
node r;
r.ans=(1ll*ans+b.ans+(sf*b.pr).a[2][1])%MOD;
r.pr=(pr)+(b.pr*ml);
r.sf=(b.sf)+(sf*b.ml);
r.ml=ml*b.ml;
return r;
}
}tr[4*M],tmp;
Matrix qkpow(Matrix a,int b)
{
Matrix r=I;
while(b>0)
{
if(b&1) r=r*a;
a=a*a;
b>>=1;
}
return r;
}
void insert(int i,int l,int r,int id,int v)
{
if(l==r)
{
Matrix t=qkpow(A,v);
tr[i].ans=t.a[2][1];
tr[i].pr=tr[i].sf=tr[i].ml=I+t;
return ;
}
int mid=(l+r)>>1;
if(mid>=id) insert(i<<1,l,mid,id,v);
else insert(i<<1|1,mid+1,r,id,v);
tr[i]=tr[i<<1]+tr[i<<1|1];
}
void ask(int i,int l,int r,int L,int R)
{
if(L>r || l>R) return ;
if(L<=l && r<=R)
{
tmp=tmp+tr[i];
return ;
}
int mid=(l+r)>>1;
ask(i<<1,l,mid,L,R);
ask(i<<1|1,mid+1,r,L,R);
}
signed main()
{
I.a[1][1]=I.a[2][2]=I.a[3][3]=1;A.a[1][3]=2;
A.a[1][1]=A.a[1][2]=A.a[2][1]=A.a[3][1]=A.a[3][3]=1;
//qkpow(A,2).print();
n=read();m=read();
for(int i=1;i<=n;i++)
insert(1,1,n,i,read());
while(m--)
{
int id=read(),l=read(),r=read();
if(id==1)
{
insert(1,1,n,l,r);
}
else
{
tmp.pr=tmp.sf=E;tmp.ml=I;tmp.ans=0;
ask(1,1,n,l,r);
printf("%d\n",tmp.ans);
}
}
}