真的吗?|

LgxTpre

园龄:2年6个月粉丝:24关注:6

题解:【THUSCH2017】 大魔法师

题目链接

前言

线段树和矩阵乘法的板子拼接题,这个题题目本身思维难度不大,但是可以给我们提供许多平时写代码的底层优化技巧。

题目思路

首先回到题目,我们需要维护 n 个魔法球,每个魔法球里面的内容有水火土三部分。第四到六个操作我们还需要维护一个系数 x,表示这个区域有多少个水晶球。这样的话我们建一棵线段树,在每个节点维护两个 1×4 的矩阵,一个是当前状态 val,另一个是懒惰标记 tag

[AiBiCix]

然后我们推一下转移矩阵:

操作一:

[AiBiCix]×Base=[Ai+BiBiCix]

Base=[1000110000100001]

操作二:

[AiBiCix]×Base=[AiBi+CiCix]

Base=[1000010001100001]

操作三:

[AiBiCix]×Base=[AiBiCi+Aix]

Base=[1010010000100001]

操作四:

[AiBiCix]×Base=[Ai+vBiCix]

Base=[100001000010v001]

操作五:

[AiBiCix]×Base=[AiBi×vCix]

Base=[10000v0000100001]

操作六:

[AiBiCix]×Base=[AiBivx]

Base=[10000100000000v1]

对于每一个操作,我们只需要用线段树的区间乘法,只不过改成乘一个矩阵就好。

询问则是一个区间求和,把要求的区间里的矩阵加起来就好了。

技巧(正题)

这道题的具体做法其他题解可能讲的更为清晰,所以我来总结一些以后编程中应该尽量去使用的小技巧。

本题其实不卡常。

先上一张图:
QQ图片20220823194004.png

那么我做了些什么呢?

不要在意全是 WA。

首先第一个是把矩阵下标从 14 改到 03但是实际上作用不大。

然后抛开矩阵乘法不看,如果只看线段树,那就是一个支持区间乘,区间求和的线段树板子。甚至比线段树二简单。但是为什么它跑的这么慢呢?就是因为每一个操作都是矩阵乘法,如果普通写是一个三重循环,而平常的线段树上传下传操作是 O(1) 的,因此我们优化的地方就分成两部分,一个是加速它的矩阵操作,一个是减少它的操作次数。

对于前者,这个题是一个 1×44×4 的矩阵相乘,我们把最内层循环展开,这样就减少了循环次数。

普通的矩阵乘法:

matrix operator * (const matrix& T) const
{
	matrix res;
	for(int i=1;i<=4;i++)
		for(int j=1;j<=4;j++)
			for(int k=1;k<=4;k++)
				res.a[i][j]=(res.a[i][j]+(1ll*a[i][k]*T.a[k][j])%mod)%mod;
	return res;
}

对于这个题的循环展开:

matrix operator * (const matrix& T) const
{
	matrix res;
	for(int i=0;i<4;i++)
		for(int j=0;j<4;j++)
			res.a[i][j]=((((1ll*a[i][0]*T.a[0][j]%mod)+1ll*a[i][1]*T.a[1][j]%mod)%mod+1ll*a[i][2]*T.a[2][j]%mod)%mod+1ll*a[i][3]*T.a[3][j]%mod)%mod;
	return res;
}

对于后者,我们容易想到平时的线段树操作是在运行标记下传的时候,如果标记里面没有东西就不用运行了,这个题我们可以再额外维护一个变量 lzy,记录这个节点有没有标记,如果这个变量为 0 就可以不用运行了。此外还可以多判断一个内容,就是如果已经到了叶子节点就不用往后传 tag 了。

还要注意的一些小细节有 tag 矩阵清零是要都设为单位矩阵 I,还有变量都设置为 int 就行,当有风险溢出时乘一个 1ll 就好了。

Code

#include<bits/stdc++.h>
#define MAX 250010
#define ll long long
#define mod 998244353
using namespace std;

inline int read()
{
	int s=0,w=1;
	char c=getchar();
	while(!isdigit(c)) {if(c=='-') w=-1; c=getchar();}
	while(isdigit(c)) s=(s<<1)+(s<<3)+(c^48),c=getchar();
	return s*w;
}

int n,m;
int op,x,y;
int a[MAX][4];

struct matrix
{
	int a[4][4];
	inline matrix()
	{
		for(int i=0;i<4;i++)
			for(int j=0;j<4;j++)
				a[i][j]=0;
	}
	matrix operator * (const matrix& T) const
	{
		matrix res;
		for(int i=0;i<4;i++)
			for(int j=0;j<4;j++)
				res.a[i][j]=((((1ll*a[i][0]*T.a[0][j]%mod)+1ll*a[i][1]*T.a[1][j]%mod)%mod+1ll*a[i][2]*T.a[2][j]%mod)%mod+1ll*a[i][3]*T.a[3][j]%mod)%mod;
		return res;
	}
	matrix operator + (const matrix& T) const
	{
		matrix res;
		for(int i=0;i<4;i++)
			for(int j=0;j<4;j++)
				res.a[i][j]=(a[i][j]+T.a[i][j])%mod;
		return res;
	}
};

matrix I,A,B,C,D,E,F;
inline void init()
{
	for(int i=0;i<4;i++)
		for(int j=0;j<4;j++)
			if(i==j)
				I.a[i][j]=1;
			else
				I.a[i][j]=0;
	A.a[0][0]=A.a[1][1]=A.a[2][2]=A.a[3][3]=A.a[1][0]=1;
	B.a[0][0]=B.a[1][1]=B.a[2][2]=B.a[3][3]=B.a[2][1]=1;
	C.a[0][0]=C.a[1][1]=C.a[2][2]=C.a[3][3]=C.a[0][2]=1;
	D.a[0][0]=D.a[1][1]=D.a[2][2]=D.a[3][3]=1;
	E.a[0][0]=E.a[2][2]=E.a[3][3]=1;
	F.a[0][0]=F.a[1][1]=F.a[3][3]=1;
	return;
}

struct Segment_Tree
{
	int l,r,lzy;
	matrix val,tag;
}t[MAX<<2];
inline void pushup(int i)
{
	t[i].val.a[0][0]=(t[i<<1].val.a[0][0]+t[i<<1|1].val.a[0][0])%mod;
	t[i].val.a[0][1]=(t[i<<1].val.a[0][1]+t[i<<1|1].val.a[0][1])%mod;
	t[i].val.a[0][2]=(t[i<<1].val.a[0][2]+t[i<<1|1].val.a[0][2])%mod;
	t[i].val.a[0][3]=(t[i<<1].val.a[0][3]+t[i<<1|1].val.a[0][3])%mod;
	return;
}
inline void pushdown(int i)
{
	if(!t[i].lzy) return;
	t[i<<1].val=t[i<<1].val*t[i].tag;
	t[i<<1|1].val=t[i<<1|1].val*t[i].tag;
	t[i<<1].tag=t[i<<1].tag*t[i].tag;
	t[i<<1|1].tag=t[i<<1|1].tag*t[i].tag;
	t[i<<1].lzy=t[i<<1|1].lzy=1;
	t[i].tag=I,t[i].lzy=0;
	return;
}
void build(int i,int l,int r)
{
	t[i].l=l,t[i].r=r,t[i].tag=I;
	if(l==r)
	{
		t[i].val.a[0][0]=a[l][1]%mod;
		t[i].val.a[0][1]=a[l][2]%mod;
		t[i].val.a[0][2]=a[l][3]%mod;
		t[i].val.a[0][3]=1;
		return;
	}
	int mid=(l+r)>>1;
	build(i<<1,l,mid);
	build(i<<1|1,mid+1,r);
	pushup(i);
	return;
}
void change(int i,int l,int r,matrix k)
{
	if(t[i].l>=l&&t[i].r<=r)
	{
		t[i].val=t[i].val*k;
		t[i].tag=t[i].tag*k;
		t[i].lzy=1;
		return;
	}
	pushdown(i);
	int mid=(t[i].l+t[i].r)>>1;
	if(l<=mid) change(i<<1,l,r,k);
	if(r>mid) change(i<<1|1,l,r,k);
	pushup(i);
	return;
}
matrix query(int i,int l,int r)
{
	if(t[i].l>=l&&t[i].r<=r) return t[i].val;
	pushdown(i);
	int mid=(t[i].l+t[i].r)>>1;
	matrix ans;
	if(l<=mid) ans=ans+query(i<<1,l,r);
	if(r>mid) ans=ans+query(i<<1|1,l,r);
	return ans;
}

int main()
{
	init();
	n=read();
	for(int i=1;i<=n;i++)
		a[i][1]=read(),a[i][2]=read(),a[i][3]=read();
	build(1,1,n);
	m=read();
	for(int i=1;i<=m;i++)
	{
		op=read(),x=read(),y=read();
		switch(op)
		{
			case 1:change(1,x,y,A);break;
			case 2:change(1,x,y,B);break;
			case 3:change(1,x,y,C);break;
			case 4:D.a[3][0]=read();change(1,x,y,D);break;
			case 5:E.a[1][1]=read();change(1,x,y,E);break;
			case 6:F.a[3][2]=read();change(1,x,y,F);break;
			case 7:matrix ans=query(1,x,y);cout<<ans.a[0][0]<<" "<<ans.a[0][1]<<" "<<ans.a[0][2]<<endl;break;
		}
	}
	return (0-0);
} 

本文作者:LgxTpre

本文链接:https://www.cnblogs.com/LittleTwoawa/p/16621116.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   LgxTpre  阅读(94)  评论(0编辑  收藏  举报
评论
收藏
关注
推荐
深色
回顶
收起
点击右上角即可分享
微信分享提示