[THUSCH 2017]大魔法师

本文介绍了一种利用矩阵操作优化线段树的算法技巧,通过实现线性代数变换,解决了传统线段树中懒标记顺序混乱的问题。文章详细阐述了如何在动态规划问题中运用树剖维护矩阵的方法,并提供了完整的代码示例。

题目

传送门 to LOJ

思路

一看就是线段树的题。甚至不少人会觉得这是黄题吧?

然而,如果用很多个 i n t \tt{int} int 来维护懒标记,顺序 不就分不清了吗?其实这么做理论上说是可以的,只是你要写的 i f \sout{if} if肯定不会少。

于是我们换一个东西,可以实现 线性代数变换,并且 满足结合律,不满足交换律(结合律使得懒标记可以叠加;不满足交换律使得顺序不同,结果不同)。这玩意儿是什么呢?

矩阵!在动态 d p \tt{dp} dp 中,我们见过这样的奇技淫巧:树剖维护矩阵。不过当时的矩阵乘法是重定义版。这里我们就可以使用普通的矩阵乘法。

维护一个向量 [ s u m a , s u m b , s u m c , s i z e ] [sum_a,sum_b,sum_c,size] [suma,sumb,sumc,size] ,剩下的事情一切好办。

代码

#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;
# define FOR(i,a,b) for(int i=(a); i<(b); ++i)
inline int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0' or c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c and c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MaxN = 300002, Mod = 998244353;

struct Matrix{
	const static int N = 4;
	int a[N][N];
	Matrix(){ memset(a,0,N*N<<2); }
	Matrix operator * (const Matrix &b)const{
		Matrix c;
		FOR(i,0,N) FOR(k,0,N) if(a[i][k] != 0) FOR(j,0,N)
			c.a[i][j] = (c.a[i][j]+1ll*a[i][k]*b.a[k][j])%Mod;
		return c;
	}
	Matrix &operator *= (const Matrix &b){
		*this = (*this)*b;
		return *this;
	}
	Matrix operator + (const Matrix &b)const{
		Matrix c; // 一定是行向量相加
		FOR(i,0,N-1) c.a[0][i] = (a[0][i]+b.a[0][i])%Mod;
		return c;
	}
	Matrix &operator += (const Matrix &b){
		*this = (*this)+b;
		return *this;
	}
	static Matrix I(){
		Matrix c;
		FOR(i,0,N) c.a[i][i] = 1;
		return c;
	}
};

int item[MaxN][3], n;

struct SegmentTree{
	struct Node{
		Matrix data, lazy;
	} node[MaxN<<2];
	# define mid ((l+r+1)>>1)
	# define lson o<<1,l,mid
	# define rson o<<1|1,mid,r
	// 左闭右开,即[l,r)
	void pushUp(int o,int l,int r){
		node[o].data = node[o<<1].data+node[o<<1|1].data;
		node[o].data.a[0][3] = r-l;
	}
	void buildTree(int o=1,int l=0,int r=n){
		if(l+1 == r) // a leaf
			FOR(i,0,3) node[o].data.a[0][i] = item[l][i];
		else
			buildTree(lson), buildTree(rson), pushUp(o,l,r);
		node[o].lazy = Matrix::I(), node[o].data.a[0][3] = r-l;
	}
	void modifyNode(int o,const Matrix &mat){
		node[o].data *= mat, node[o].lazy *= mat;
	}
	void pushDown(int o){
		modifyNode(o<<1,node[o].lazy);
		modifyNode(o<<1|1,node[o].lazy);
		node[o].lazy = Matrix::I();
	}
	void modify(int ql,int qr,const Matrix &mat,int o=1,int l=0,int r=n){
		if(ql <= l and r <= qr) return modifyNode(o,mat);
		pushDown(o);
		if(ql < mid) modify(ql,qr,mat,lson);
		if(mid < qr) modify(ql,qr,mat,rson);
		pushUp(o,l,r);
	}
	Matrix Query(int ql,int qr,int o=1,int l=0,int r=n){
		if(ql <= l and r <= qr) return node[o].data;
		pushDown(o); Matrix res;
		if(ql < mid) res += Query(ql,qr,lson);
		if(mid < qr) res += Query(ql,qr,rson);
		return res;
	}
	# undef mid
	# undef lson
	# undef rson
} ppl;

Matrix mat[10];

int main(){
	n = readint();
	FOR(i,0,n) FOR(j,0,3) item[i][j] = readint();
	ppl.buildTree();

	FOR(i,1,7) mat[i] = Matrix::I();
	mat[1].a[1][0] = 1; // A += B
	mat[2].a[2][1] = 1; // B += C
	mat[3].a[0][2] = 1; // C += A
	// mat[4].a[3][0] = v; // A += size*v
	// mat[5].a[1][1] = v; // B *= v
	mat[6].a[2][2] = 0; // C = 0
	// mat[6].a[3][2] = v; // C += size*v

	for(int m=readint(),opt,l,r,v; m; --m){
		opt = readint(), l = readint()-1, r = readint();
		if(4 <= opt and opt <= 6){
			v = readint();
			mat[4].a[3][0] = v;
			mat[5].a[1][1] = v;
			mat[6].a[3][2] = v;
		}
		if(opt != 7) ppl.modify(l,r,mat[opt]);
		else{
			auto ans = ppl.Query(l,r);
			FOR(i,0,3) printf("%d ",ans.a[0][i]);
			putchar('\n');
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值