大魔法师

一、题目

点此看题

二、解法

很容易看出来是线段树打标记的题,但是标记的顺序就很恶心。

有一个很简洁的解法,就是把所有的修改操作看做矩阵乘法,标记也用矩阵维护,我们的值用一个 4 × 1 4\times1 4×1的矩阵维护(设 A A A是值, B B B是操作矩阵,用 B × A B\times A B×A更新值):
A B C l e n \begin{matrix}A\\B\\C\\len\end{matrix} ABClen然后操作分别用下列矩阵维护(顺序是操作编号 1 − 6 1-6 16):
1 1 0 0 0 1 0 0 0 0 1 0 0 0 0 1 \begin{matrix}1&1&0&0\\0&1&0&0\\0&0&1&0\\0&0&0&1\end{matrix} 1000110000100001 1 0 0 0 0 1 1 0 0 0 1 0 0 0 0 1 \begin{matrix}1&0&0&0\\0&1&1&0\\0&0&1&0\\0&0&0&1\end{matrix} 1000010001100001 1 0 0 0 0 1 0 0 1 0 1 0 0 0 0 1 \begin{matrix}1&0&0&0\\0&1&0&0\\1&0&1&0\\0&0&0&1\end{matrix} 1010010000100001 1 0 0 v 0 1 0 0 0 0 1 0 0 0 0 1 \begin{matrix}1&0&0&v\\0&1&0&0\\0&0&1&0\\0&0&0&1\end{matrix} 100001000010v001 1 0 0 0 0 v 0 0 0 0 1 0 0 0 0 1 \begin{matrix}1&0&0&0\\0&v&0&0\\0&0&1&0\\0&0&0&1\end{matrix} 10000v0000100001 1 0 0 0 0 1 0 0 0 0 0 v 0 0 0 1 \begin{matrix}1&0&0&0\\0&1&0&0\\0&0&0&v\\0&0&0&1\end{matrix} 10000100000000v1最后一个问题就是标记顺序,矩阵是可以直接用乘法来叠加的,按照我上面的构造方法,设新加入的矩阵为 A A A,已经打过标记的矩阵是 B B B,那么存下来的矩阵应该是 A × B A\times B A×B(本质上式矩阵乘法没有交换律)。本题需要卡常,详见代码。

#pragma GCC optimize(2)
#include 
#include 
const int M = 250005;
const int MOD = 998244353;
int read()
{
    int x=0,flag=1;
    char c;
    while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
    while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*flag;
}
int n,m,op,l,r,v;
struct Matrix
{
    int n,m,a[5][5];
    Matrix()
    {
        n=m=0;
        memset(a,0,sizeof a);
    }
    void reset()
    {
        for(int i=1; i<=4; i++) for(int j=1; j<=4; j++) a[i][j]=(i==j);
    }
    void clear()
    {
        memset(a,0,sizeof a);
    }
    Matrix operator * (const Matrix &B) const
    {
        Matrix R;
        R.n=n;
        R.m=B.m;
        for(int i=1; i<=n; i++)
            for(int j=1; j<=m; j++)
            {
                if(a[i][j]==0) continue;
                for(int k=1; k<=B.m; k++)
                    R.a[i][k]=(R.a[i][k]+1ll*a[i][j]*B.a[j][k])%MOD;
            }
        return R;
    }
    Matrix operator + (const Matrix &B) const
    {
        Matrix R;
        R.n=n;
        R.m=m;
        for(int i=1; i<=n; i++)
            for(int j=1; j<=m; j++)
                R.a[i][j]=(a[i][j]+B.a[i][j])%MOD;
        return R;
    }
} A,emp;
struct node
{
    Matrix val,la;
    node()
    {
        val.m=1;
        val.n=la.n=la.m=4;
        la.reset();
    }
} tr[M*4];
void build(int i,int l,int r)
{
    if(l==r)
    {
        tr[i].val.a[1][1]=read();
        tr[i].val.a[2][1]=read();
        tr[i].val.a[3][1]=read();
        tr[i].val.a[4][1]=1;
        return ;
    }
    int mid=(l+r)>>1;
    build(i<<1,l,mid);
    build(i<<1|1,mid+1,r);
    tr[i].val=tr[i<<1].val+tr[i<<1|1].val;
}
void down(int i)
{
    int ls=i<<1,rs=i<<1|1;
    tr[ls].val=tr[i].la*tr[ls].val;
    tr[rs].val=tr[i].la*tr[rs].val;
    tr[ls].la=tr[i].la*tr[ls].la;
    tr[rs].la=tr[i].la*tr[rs].la;
    tr[i].la.reset();
}
void updata(int i,int l,int r,int L,int R)
{
    if(l>R || L>r) return ;
    if(L<=l && r<=R)
    {
        tr[i].la=A*tr[i].la;
        tr[i].val=A*tr[i].val;
        return ;
    }
    int mid=(l+r)>>1;
    down(i);
    updata(i<<1,l,mid,L,R);
    updata(i<<1|1,mid+1,r,L,R);
    tr[i].val=tr[i<<1].val+tr[i<<1|1].val;
}
Matrix query(int i,int l,int r,int L,int R)
{
    if(l>R || L>r) return emp;
    if(L<=l && r<=R) return tr[i].val;
    int mid=(l+r)>>1;
    down(i);
    return query(i<<1,l,mid,L,R)+query(i<<1|1,mid+1,r,L,R);
}
signed main()
{
    A.n=A.m=emp.n=4;
    emp.m=1;
    n=read();
    build(1,1,n);
    m=read();
    for(int i=1; i<=m; i++)
    {
        op=read();
        l=read();
        r=read();
        A.reset();
        if(op==1)
            A.a[1][2]=1;
        if(op==2)
            A.a[2][3]=1;
        if(op==3)
            A.a[3][1]=1;
        if(op==4)
            A.a[1][4]=v=read();
        if(op==5)
            A.a[2][2]=v=read();
        if(op==6)
            A.a[3][3]=0,A.a[3][4]=v=read();
        if(op!=7)
            updata(1,1,n,l,r);
        if(op==7)
        {
            Matrix t=query(1,1,n,l,r);
            printf("%d %d %d\n",t.a[1][1],t.a[2][1],t.a[3][1]);
        }
    }
}

你可能感兴趣的:(线段树,矩阵加速)