zoukankan      html  css  js  c++  java
  • THUSCH 2017 大魔*(矩阵乘法+线段树)

    题意

    https://loj.ac/problem/2980

    思路

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

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

    操作一 (Aleftarrow A+B)

    相当乘上于一个这样的矩阵

    [egin{pmatrix} 1&0&0\ 1&1&0\ 0&0&1 end{pmatrix} ]

    操作二 (Bleftarrow B+C)

    相当于乘上一个这样的矩阵

    [egin{pmatrix} 1&0&0\ 0&1&0\ 0&1&1 end{pmatrix} ]

    操作三 (Cleftarrow C+A)

    相当于乘上一个这样的矩阵

    [egin{pmatrix} 1&0&1\ 0&1&0\ 0&0&1 end{pmatrix} ]

    操作四 (Aleftarrow A+v)

    相当于加上一个这样的矩阵

    [egin{pmatrix} v\ 0\ 0 end{pmatrix} ]

    操作五 (Bleftarrow B cdot v)

    相当于乘上一个这样的矩阵

    [egin{pmatrix} 1&0&0\ 0&v&0\ 0&0&1 end{pmatrix} ]

    操作六 (C leftarrow v)

    相当于乘上一个全零的 (3 imes 3) 矩阵,再加上一个这样的矩阵

    [egin{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
    ",pls[1][1],pls[1][2],pls[1][3]);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    洛谷3004 [USACO10DEC]宝箱Treasure Chest
    洛谷3778 [APIO2017]商旅
    洛谷4141消失之物——每个体积的角度
    洛谷2943 [USACO09MAR]清理Cleaning Up——转变枚举内容的dp
    bzoj1858[Scoi2010]序列操作
    poj1325机器工作——二分图最小点覆盖
    洛谷P1144——最短路计数
    poj3254二进制放牛——状态压缩DP
    poj1191棋盘分割——区间DP
    洛谷P1474货币系统——背包方案计数
  • 原文地址:https://www.cnblogs.com/Paulliant/p/10570392.html
Copyright © 2011-2022 走看看