题意

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;
}

THUSCH 2017 大魔法师(矩阵乘法+线段树)的更多相关文章

  1. Luogu P4643 【模板】动态dp(矩阵乘法,线段树,树链剖分)

    题面 给定一棵 \(n\) 个点的树,点带点权. 有 \(m\) 次操作,每次操作给定 \(x,y\) ,表示修改点 \(x\) 的权值为 \(y\) . 你需要在每次操作之后求出这棵树的最大权独立集 ...

  2. ZOJ 2671 Cryptography 矩阵乘法+线段树

    B - Cryptography Time Limit:5000MS     Memory Limit:32768KB     64bit IO Format:%lld & %llu Subm ...

  3. HDU 6155 Subsequence Count(矩阵乘法+线段树+基础DP)

    题意 给定一个长度为 \(n\) 的 \(01\) 串,完成 \(m\) 种操作--操作分两种翻转 \([l,r]\) 区间中的元素.求区间 \([l,r]\) 有多少个不同的子序列. \(1 \le ...

  4. HDU 3074.Multiply game-区间乘法-线段树(单点更新、区间查询),上推标记取模

    Multiply game Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Tot ...

  5. codeforces750E New Year and Old Subsequence 矩阵dp + 线段树

    题目传送门 思路: 先看一个大牛的题解 题解里面对矩阵的构造已经写的很清楚了,其实就是因为在每个字符串都有固定的很多中状态,刚好可以用矩阵来表达,所以$(i,j)$这种状态可以通过两个相邻的矩阵的$m ...

  6. HDU 6155 Subsequence Count(矩阵 + DP + 线段树)题解

    题意:01串,操作1:把l r区间的0变1,1变0:操作2:求出l r区间的子序列种数 思路:设DP[i][j]为到i为止以j结尾的种数,假设j为0,那么dp[i][0] = dp[i - 1][1] ...

  7. Weak Pair---hud5877大连网选(线段树优化+dfs)

    题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5877  题意:给你一颗树,有n个节点,每个节点都有一个权值v[i]:现在求有多少对(u,v ...

  8. LOJ#6048. 「雅礼集训 2017 Day10」数列(线段树)

    题面 传送门 题解 我的做法似乎非常复杂啊-- 首先最长上升子序列长度就等于把它反过来再接到前面求一遍,比方说把\(2134\)变成\(43122134\),实际上变化之后的求一个最长上升子序列和方案 ...

  9. BZOJ4869 六省联考2017相逢是问候(线段树+欧拉函数)

    由扩展欧拉定理,a^(a^(a^(……^x)))%p中x作为指数的模数应该是φ(φ(φ(φ(……p)))),而p取log次φ就会变为1,也即每个位置一旦被修改一定次数后就会变为定值.线段树维护区间剩余 ...

随机推荐

  1. C# 使用MongoDB遇到的问题

    错误:缺少 System.Runtime.InteropServices.RuntimeInformation.dll version = 4.0.0 解决方法: 下载了System.Runtime. ...

  2. 魔方---java

    package LMS; import java.awt.AWTException;import java.awt.Robot;import java.awt.event.InputEvent;imp ...

  3. python多进程multiprocessing模块中Queue的妙用

    最近的部门RPA项目中,小爬为了提升爬虫性能,使用了Python中的多进程(multiprocessing)技术,里面需要用到进程锁Lock,用到进程池Pool,同时利用map方法一次构造多个proc ...

  4. webclient 操作超时

    Client.Headers.Add("user-agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:30.0) Gecko/ ...

  5. 取模性质,快速幂,快速乘,gcd和最小公倍数

    一.取模运算 取模(取余)运算法则: 1. (a+b)%p=(a%p+b%p)%p; 2.(a-b)%p=(a%p-b%p)%p; 3.(a*b)%p=(a%p * b%p)%p; 4.(a^b)%p ...

  6. 光圈,快门, 曝光,焦距, ISO,景深。

    光圈,快门, 曝光,焦距, ISO,景深. ISO(感光度)与图片质量 ISO -- 感光度,是一个曝光率极高的词,我们在超市买饼干的时候就可能会看见包装袋上写:本公司已通过ISO9001质量体系认证 ...

  7. 用Volume在主机和Docker容器文件传输

    1.使用Volume在主机和容器之间传输文件. 在官方文档中可以看到使用如下命令即可创建一个volume: Create a volume: $ docker volume create my-vol ...

  8. 解决postman环境切换,自动获取api签名时间及签名

    postman调试api接口时,常遇到两个问题: 1.环境分为开发环境,测试环境,正式环境,如何只写一个接口,通过切换postman环境来实现不同环境的接口调用? 2. api接口请求时往往会添加,来 ...

  9. stm32 堆溢出

    STM32 堆溢出 遇到的问题 最近在给旧项目添加了段代码,程序经常到某个状态就突然崩溃了,也不一定是在运行新代码的时候崩溃.检查了几遍代码,数组越界访问,除数为0,内存泄露等常见的问题都不存在. 原 ...

  10. Docker从零构建php-nginx-alpine镜像

    虽然之前也曾用docker环境运行了一些项目,但对于镜像这块还不是很理解,且鉴于网上现成的镜像都包含太多用不到的库,所以决定从零开始构建一个自己的镜像. alpine linux为基础镜像 docke ...