[nowcoder7611C]斐波

280 篇文章 1 订阅
137 篇文章 1 订阅

题目

传送门 to nowcoder

思路

真的是科技题啊。而且 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} fi12 f i    2 f_i^{\;2} fi2 f i − 1 f i f_{i-1}f_{i} fi1fi,很容易转移到下一个状态,这里 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} TSAxTx,也就是
∑ 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) TS(xTAx)=xS(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\}) lijrf({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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值