题目
思路
真的是科技题啊。而且 H a n d I n D e v i l \sf HandInDevil HandInDevil 直接切了, o r z orz orz
最初我以为可以直接把整个式子拿去化简。想想也不可能嘛,我们至少要解决 l = r l=r l=r 的询问吧?所以还是考虑 f ( S ) f(S) f(S) 如何变换。
然后下一步就说不清是为什么了。总之就是要这样变换。把斐波那契数列用 矩阵 来表示。维护 f i − 1 2 f_{i-1}^{\;2} fi−12 和 f i 2 f_i^{\;2} fi2 和 f i − 1 f i f_{i-1}f_{i} fi−1fi,很容易转移到下一个状态,这里 f i f_i fi 是斐波那契数列。 那么 f i b ( s u m T ) fib(sum_T) fib(sumT) 就转化为了求 A s u m T A^{sum_T} AsumT,其中 A A A 是一个矩阵。
矩阵跟实数是类似的。要求
∑
T
⫅
S
A
∑
x
∈
T
x
\sum_{T\subseteqq S}A^{\sum_{x\in T}x}
∑T⫅SA∑x∈Tx,也就是
∑
T
⫅
S
(
∏
x
∈
T
A
x
)
=
∏
x
∈
S
(
I
+
A
x
)
\sum_{T\subseteqq S}\left(\prod_{x\in T}A^{x}\right)=\prod_{x\in S}\left(I+A^{x}\right)
T⫅S∑(x∈T∏Ax)=x∈S∏(I+Ax)
其中 I I I 是单位矩阵。等式显然成立。非要补个严谨证明就归纳法。
所以我们回头看询问 ∑ l ⩽ i ⩽ j ⩽ r f ( { a i , … , a j } ) \sum_{l\leqslant i\leqslant j\leqslant r} f(\{a_i,\dots,a_j\}) ∑l⩽i⩽j⩽rf({ai,…,aj}) 其实就是问每个子区间的乘积的和。线段树显然可以维护,只需要维护四个值:区间乘积;前缀乘积和;后缀乘积和;答案(每个子区间的乘积和)。
于是本题就做完了。时间复杂度 O [ ( n + q ) log n ] \mathcal O[(n+q)\log n] O[(n+q)logn],常数高达 4 × ( 3 3 + 3 2 ) = 144 4\times(3^3+3^2)=144 4×(33+32)=144,因为区间合并时有 4 4 4 次矩阵乘法与矩阵加法。
代码
#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long int_;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
inline void writeint(int x){
if(x > 9) writeint(x/10);
putchar((x-x/10*10)^48);
}
const int Mod = 998244353;
inline int addInt(int a,const int &b){
return (a += b) >= Mod ? (a -= Mod) : a;
}
struct Matrix{
static const int N = 3;
int a[N][N];
void clear(){ memset(a,0,N*N<<2); }
static Matrix I(){
Matrix c; c.clear();
rep(i,0,N-1) c.a[i][i] = 1;
return c;
}
Matrix operator + (const Matrix &b) const {
Matrix c;
rep(i,0,N-1) rep(j,0,N-1)
c.a[i][j] = addInt(a[i][j],b.a[i][j]);
return c;
}
Matrix operator * (const Matrix &b) const {
Matrix c; c.clear();
rep(i,0,N-1) rep(j,0,N-1) rep(k,0,N-1)
c.a[i][k] = (c.a[i][k]+int_(a[i][j])*b.a[j][k])%Mod;
return c;
}
Matrix operator += (const Matrix &b){
return *this = *this+b;
}
};
Matrix qkpow(Matrix b,int q){
Matrix a = b; -- q;
for(; q; q>>=1,b=b*b)
if(q&1) a = a*b;
return a;
}
const int MaxN = 100005;
struct Status{
Matrix l, r, all, ans;
Status operator + (const Status &t) const {
Status res; res.all = all*t.all;
res.l = l+all*t.l, res.r = r*t.all+t.r;
res.ans = ans+t.ans+r*t.l; return res;
}
void init(const Matrix &mat){
l = r = all = ans = mat;
}
};
int n; // length of segment tree
namespace SgTree{
Status val[MaxN<<2];
# define __ROOT int o=1,int l=1,int r=n
# define LSON o<<1,l,(l+r)>>1
# define RSON o<<1|1,((l+r)>>1)+1,r
void modify(int qid,const Matrix &qv,__ROOT){
if(l == r) return val[o].init(qv);
if(qid <= ((l+r)>>1))
modify(qid,qv,LSON);
else modify(qid,qv,RSON);
val[o] = val[o<<1]+val[o<<1|1];
}
Status query(int ql,int qr,__ROOT){
if(ql <= l && r <= qr) return val[o];
if(qr <= ((l+r)>>1))
return query(ql,qr,LSON);
if(ql > ((l+r)>>1))
return query(ql,qr,RSON);
return query(ql,qr,LSON)+query(ql,qr,RSON);
}
}
Matrix mat, qv;
int main(){
n = readint();
int q = readint();
mat.clear(); // good habit
rep(i,0,2) rep(j,2-i,2)
mat.a[i][j] = 1;
mat.a[1][2] = 2; // cross
rep(i,1,n){
int a = readint();
qv = qkpow(mat,a);
qv = qv+Matrix::I();
SgTree::modify(i,qv);
}
for(int opt,x,y; q; --q){
opt = readint();
x = readint(), y = readint();
if(opt == 1){
qv = qkpow(mat,y);
qv = qv+Matrix::I();
SgTree::modify(x,qv);
}
else{
Matrix ans = (SgTree::query(x,y)).ans;
writeint(ans.a[2][0]); putchar('\n');
}
}
return 0;
}