题目
思路
一看就是线段树的题。甚至不少人会觉得这是黄题吧?
然而,如果用很多个
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;
}

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

被折叠的 条评论
为什么被折叠?



