THUSCH 2017 大魔法师(矩阵乘法+线段树)

题意

https://loj.ac/problem/2980

思路

区间修改考虑用线段树维护。由于一段区间的 \(A,B,C\) 可以表示成由原来的 \(A,B,C\) 乘上带上系数再加上某一个某个常数,不妨用矩阵来形象的表示这个转移。

在线段树的每一个节点上,用一个 \(1\times 3\) 的矩阵 \(\begin{pmatrix}A &B&C\end{pmatrix}\) 表示这个区间的 \(A,B,C\) 之和。用一个 \(3\times 3\) 的矩阵代表一个矩阵乘法的懒惰标记,用一个 \(1\times 3\) 的矩阵代表一次矩阵加法的懒惰标记。

操作一 \(A\leftarrow A+B\)

相当乘上于一个这样的矩阵
\[ \begin{pmatrix} 1&0&0\\ 1&1&0\\ 0&0&1 \end{pmatrix} \]
操作二 \(B\leftarrow B+C\)

相当于乘上一个这样的矩阵
\[ \begin{pmatrix} 1&0&0\\ 0&1&0\\ 0&1&1 \end{pmatrix} \]
操作三 \(C\leftarrow C+A\)

相当于乘上一个这样的矩阵
\[ \begin{pmatrix} 1&0&1\\ 0&1&0\\ 0&0&1 \end{pmatrix} \]
操作四 \(A\leftarrow A+v\)

相当于加上一个这样的矩阵
\[ \begin{pmatrix} v\\ 0\\ 0 \end{pmatrix} \]
操作五 \(B\leftarrow B \cdot v\)

相当于乘上一个这样的矩阵
\[ \begin{pmatrix} 1&0&0\\ 0&v&0\\ 0&0&1 \end{pmatrix} \]
操作六 \(C \leftarrow v\)

相当于乘上一个全零的 \(3\times 3\) 矩阵,再加上一个这样的矩阵
\[ \begin{pmatrix} 0\\ 0\\ v \end{pmatrix} \]
注意再作加法时,将总和矩阵与加标记矩阵做一次矩阵加法(总和矩阵需要乘上区间长度,与维护普通整数的线段树类似),作乘法时需要将总和矩阵、加标记矩阵、乘标记矩阵都做一次矩阵乘法。要注意利用封装使得和线段树的看起来和区间加值、区间乘值、区间求和的线段树没有区别,自然就能写好。

代码

#include<bits/stdc++.h>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
template<typename T,typename _T>inline bool chk_min(T &x,const _T y){return y<x?x=y,1:0;}
template<typename T,typename _T>inline bool chk_max(T &x,const _T y){return x<y?x=y,1:0;}
typedef long long ll;
const int N=2.5e5+5;
const int P=998244353;
struct Matrix
{
    int n,m;
    int a[3][3];
    void resize(int _n,int _m){n=_n,m=_m;}
    int *operator [](const int x){return a[x-1]-1;}
    Matrix operator +(const Matrix &_)const
    {
        Matrix res;
        res.resize(n,m);
        FOR(i,1,n)FOR(j,1,m)res[i][j]=(a[i-1][j-1]+_.a[i-1][j-1])%P;
        return res;
    }
    Matrix operator *(const Matrix &_)const
    {
        Matrix res;
        res.resize(n,_.m);
        FOR(i,1,n)FOR(j,1,_.m)
        {
            res[i][j]=0;
            FOR(k,1,m)res[i][j]=(res[i][j]+(ll)a[i-1][k-1]*_.a[k-1][j-1])%P;
        }
        return res;
    }
    Matrix operator *(const int &x)const
    {
        Matrix res;
        res.resize(n,m);
        FOR(i,1,n)FOR(j,1,m)res[i][j]=(ll)a[i-1][j-1]*x%P;
        return res;
    }
};
Matrix sum[N<<2],mul[N<<2],pls[N<<2];
void multiplied(int k,Matrix &x)
{
    sum[k]=sum[k]*x;
    mul[k]=mul[k]*x;
    pls[k]=pls[k]*x;
}
void plused(int k,Matrix &x,int l,int r)
{
    sum[k]=sum[k]+x*(r-l+1);
    pls[k]=pls[k]+x;
}
void push_up(int k)
{
    sum[k]=sum[k<<1]+sum[k<<1|1];
}
void push_down(int k,int l,int r)
{
    bool m=0,p=0;
    FOR(i,1,3)FOR(j,1,3)if(mul[k][i][j]!=(i==j)){m=1;break;}
    FOR(i,1,3)if(pls[k][1][i]){p=1;break;}
    int mid=(l+r)>>1;
    if(m)
    {
        multiplied(k<<1,mul[k]);
        multiplied(k<<1|1,mul[k]);
        FOR(i,1,3)FOR(j,1,3)mul[k][i][j]=(i==j);
    }
    if(p)
    {
        plused(k<<1,pls[k],l,mid);
        plused(k<<1|1,pls[k],mid+1,r);
        FOR(i,1,3)pls[k][1][i]=0;
    }
}
void build(int k,int l,int r)
{
    sum[k].resize(1,3),mul[k].resize(3,3),pls[k].resize(1,3);
    FOR(i,1,3)pls[k][1][i]=0;
    FOR(i,1,3)FOR(j,1,3)mul[k][i][j]=(i==j);
    if(l==r)
    {
        scanf("%d%d%d",&sum[k][1][1],&sum[k][1][2],&sum[k][1][3]);
        return;
    }
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    push_up(k);
}
void update_mul(int k,int L,int R,Matrix &x,int l,int r)
{
    if(L<=l&&r<=R)
    {
        multiplied(k,x);
        return;
    }
    push_down(k,l,r);
    int mid=(l+r)>>1;
    if(L<=mid)update_mul(k<<1,L,R,x,l,mid);
    if(R>mid)update_mul(k<<1|1,L,R,x,mid+1,r);
    push_up(k);
}
void update_pls(int k,int L,int R,Matrix &x,int l,int r)
{
    if(L<=l&&r<=R)
    {
        plused(k,x,l,r);
        return;
    }
    push_down(k,l,r);
    int mid=(l+r)>>1;
    if(L<=mid)update_pls(k<<1,L,R,x,l,mid);
    if(R>mid)update_pls(k<<1|1,L,R,x,mid+1,r);
    push_up(k);
}
Matrix query(int k,int L,int R,int l,int r)
{
    if(L<=l&&r<=R)return sum[k];
    push_down(k,l,r);
    int mid=(l+r)>>1;
    if(R<=mid)return query(k<<1,L,R,l,mid);
    else if(L>mid)return query(k<<1|1,L,R,mid+1,r);
    else return query(k<<1,L,R,l,mid)+query(k<<1|1,L,R,mid+1,r);
}
int n,m;

int main()
{
    scanf("%d",&n);
    build(1,1,n);
    scanf("%d",&m);
    FOR(i,1,m)
    {
        Matrix mul,pls;
        mul.resize(3,3),pls.resize(1,3);
        int op,l,r,val;
        scanf("%d%d%d",&op,&l,&r);
        if(op>=4&&op<=6)scanf("%d",&val);
        if(op>=1&&op<=3)
        {
            FOR(i,1,3)FOR(j,1,3)mul[i][j]=(i==j);
            if(op==1)mul[2][1]=1;
            else if(op==2)mul[3][2]=1;
            else if(op==3)mul[1][3]=1;
            update_mul(1,l,r,mul,1,n);
        }
        else if(op==4)
        {
            pls[1][1]=val,pls[1][2]=pls[1][3]=0;
            update_pls(1,l,r,pls,1,n);
        }
        else if(op==5)
        {
            FOR(i,1,3)FOR(j,1,3)mul[i][j]=(i==j);
            mul[2][2]=val;
            update_mul(1,l,r,mul,1,n);
        }
        else if(op==6)
        {
            FOR(i,1,3)FOR(j,1,3)mul[i][j]=(i==j);
            mul[3][3]=0;
            pls[1][1]=0,pls[1][2]=0,pls[1][3]=val;
            update_mul(1,l,r,mul,1,n);
            update_pls(1,l,r,pls,1,n);
        }
        else if(op==7)
        {
            pls=query(1,l,r,1,n);
            printf("%d %d %d\n",pls[1][1],pls[1][2],pls[1][3]);
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/Paulliant/p/10570392.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值