P7453 [THUSCH2017] 大魔法师

原题链接

逆天卡常题,十几次提交终于过了!

首先看见题目就知道这是道数据结构题,但发现元素之间会有影响,如果直接维护的话无法维护,那么这时矩阵就排上用场了。线段树套矩阵,可以方便地维护一些元素之间有影响的问题。

首先我们每个叶子节点都存放一个矩阵,对于下标 i i i,它的矩阵就是 [ A B C 1 ] \begin{bmatrix} A& B& C&1 \end{bmatrix} [ABC1],因为我们还要考虑 v v v 值的修改。接下来 6 6 6 种操作,我们推出各自对应的矩阵,然后线段树维护区间矩阵乘,维护乘法标记和区间和。这些操作和普通线段树的一样。注意标记初始化为单位矩阵,矩阵乘法没有交换律,注意顺序。

接下来还有卡常部分。

首先矩阵乘法中的循环要展开,然后 pushdown 操作时判断 tag 是否为单位矩阵,若是单位矩阵直接 return,取模时用减法取模,不要全开 long long,这四个是最关键的。

还有一些不那么重要的有如先设一个单位矩阵 I I I,然后初始化时直接等于 I I I 就可以了;线段树的矩阵维护 1 × 4 1\times 4 1×4 的,减少运算量。

最终卡了半天的代码如下:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rd read()
const int N=3e5+10,mod=998244353;
namespace IO{
    inline int read(){
        int x=0;
        char ch=getchar();
        while(!isdigit(ch)) ch=getchar();
        while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
        return x;
    }
    template <typename T> inline void write(T x){
        if(x>9) write(x/10);
        putchar(x%10+'0');
    }
}
using namespace IO;
int MOD(int x){return (x>mod)?x-mod:x;}
int n,aa[N],bb[N],cc[N],m;
struct matrix{
	int a[4][4];
	matrix(){memset(a,0,sizeof(a));}
	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]=MOD(1ll*a[i][j]+T.a[i][j]);
		return res;
	}
	matrix operator *(const matrix& T)const{
		matrix res;
		for(int i=0;i<4;i++){
			for(int k=0;k<4;k++){
				if(!a[i][k]) continue;
				for(int j=0;j<4;j++){
					if(!T.a[k][j]) continue;
					res.a[i][j]=MOD(res.a[i][j]+1ll*a[i][k]*T.a[k][j]%mod);
				}
			}
		}
		return res;
	}
	bool operator ==(const matrix& T)const{
		for(int i=0;i<4;i++) for(int j=0;j<4;j++) if(a[i][j]!=T.a[i][j]) return false;
		return true;
	}
}A0,B0,C0,A1,B1,C1;
int I_[4][4]={
{1,0,0,0},
{0,1,0,0},
{0,0,1,0},
{0,0,0,1},
};
matrix I;
void Init(){
	for(int i=0;i<4;i++) for(int j=0;j<4;j++) I.a[i][j]=I_[i][j];
}
matrix sum[N<<2],tag[N<<2];
void pushdown(int u){
	if(tag[u]==I) return;
	tag[u<<1]=tag[u<<1]*tag[u],tag[u<<1|1]=tag[u<<1|1]*tag[u];
	sum[u<<1]=sum[u<<1]*tag[u],sum[u<<1|1]=sum[u<<1|1]*tag[u];
	tag[u]=I;
}
void pushup(int u){
	sum[u]=sum[u<<1]+sum[u<<1|1];
}
void build(int u,int l,int r){
	tag[u]=I;
	if(l==r){sum[u].a[0][0]=aa[l],sum[u].a[0][1]=bb[l],sum[u].a[0][2]=cc[l],sum[u].a[0][3]=1;return;}
	int mid=(l+r)>>1;
	build(u<<1,l,mid),build(u<<1|1,mid+1,r);
	pushup(u);
}
void modify(int u,int l,int r,int ql,int qr,matrix v){
	if(l>=ql&&r<=qr){
		tag[u]=tag[u]*v,sum[u]=sum[u]*v;
		return;
	}
	pushdown(u);int mid=(l+r)>>1;
	if(ql<=mid) modify(u<<1,l,mid,ql,qr,v);
	if(qr>mid) modify(u<<1|1,mid+1,r,ql,qr,v);
	pushup(u);
}
matrix query(int u,int l,int r,int ql,int qr){
	if(l>=ql&&r<=qr) return sum[u];
	pushdown(u);int mid=(l+r)>>1;matrix res;
	if(ql<=mid) res=res+query(u<<1,l,mid,ql,qr);
	if(qr>mid) res=res+query(u<<1|1,mid+1,r,ql,qr);
	return res;
}
void init(){
	for(int i=0;i<4;i++){
		A0.a[i][i]=A1.a[i][i]=1;
		B0.a[i][i]=B1.a[i][i]=1;
		C0.a[i][i]=C1.a[i][i]=1;
	}
	A0.a[1][0]=1,B0.a[2][1]=1,C0.a[0][2]=1,C1.a[2][2]=0;
}
int main(){
	n=rd;Init(),init();
	for(int i=1;i<=n;i++) aa[i]=rd,bb[i]=rd,cc[i]=rd;
	build(1,1,n),m=rd;
	while(m--){
		int opt=rd,l=rd,r=rd;
		if(opt==1) modify(1,1,n,l,r,A0);
		else if(opt==2) modify(1,1,n,l,r,B0);
		else if(opt==3) modify(1,1,n,l,r,C0);
		else if(opt<=6){
			int v=rd;
			if(opt==4) A1.a[3][0]=v,modify(1,1,n,l,r,A1);
			else if(opt==5) B1.a[1][1]=v,modify(1,1,n,l,r,B1);
			else C1.a[3][2]=v,modify(1,1,n,l,r,C1);
		}
		else{
			matrix res=query(1,1,n,l,r);
			printf("%d %d %d\n",res.a[0][0],res.a[0][1],res.a[0][2]);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值