我一脚踹他下去。

先致敬 houzhe 学长经典:

我看到我的队友写了个又臭又长的线段树,维护了一堆 tag,于是一脚把他踹下去,写了个线段树维护矩阵,然后就过了。

回到这题,题意即为求一段连续的版本 [x,y] 中,所有版本的区间 [l,r] 的值的平方和。

首先显然可以变成 [1,y] 版本的答案减去 [1,x] 版本的答案。然后变成一个历史版本和问题。解决历史版本和问题自然会想到维护矩阵。当然你打 tag 也是可以的,但是这样的话建议还是从矩阵的角度去推更新,否则很容易出问题。

具体地,SGT 上每个位置维护一个 4×4 的矩阵,记录区间和,区间平方和,区间长度和区间历史版本和四个量,把 (ai+k)2 套路化地拆成 ai2+ai×k+len×k2,这也是一个矩阵很好维护的形式。每做完一次操作之后更新历史和即可。

细节不是很多,需要注意的有:

  • tag 的矩阵初始值是单位矩阵而不是全 0 矩阵。

  • ai 和更新的 x 都可能是负数。

结束了。跑的还挺快……吗?至少过了而且不是最劣解。/kk

code:

点击查看代码
int n,m,q,a[N],ans[N];
struct node{int l,r,x;}b[N];
struct Node{int l,r,s,id;};
vector<Node> g[N];
il int Mod(int x,int y){return x+y>=mod?x+y-mod:x+y;}
struct SGT{
	struct mat{
		int a[4][4];
		mat(){mems(a,0);}
		il void reset(){mems(a,0);rep(i,0,3)a[i][i]=1;}
		il mat operator*(const mat &rhs)const{
			mat r;
			rep(k,0,3)rep(i,0,3)rep(j,0,3)r.a[i][j]=Mod(r.a[i][j],1ll*a[i][k]*rhs.a[k][j]%mod);
			return r;
		}
		il mat operator+(const mat &rhs)const{
			mat r;
			rep(i,0,3)rep(j,0,3)r.a[i][j]=Mod(a[i][j],rhs.a[i][j]);
			return r;
		}
	};
	struct Tnode{mat x,tag;}tr[N<<2];
	il void pushup(int o){tr[o].x=tr[o<<1].x+tr[o<<1|1].x;}
	il void reset(int o,mat &k){tr[o].x=tr[o].x*k,tr[o].tag=tr[o].tag*k;}
	il void pushdown(int o){reset(o<<1,tr[o].tag),reset(o<<1|1,tr[o].tag),tr[o].tag.reset();}
	void build(int l,int r,int o){
		tr[o].tag.reset();
		if(l==r)return tr[o].x.a[0][0]=Mod(a[l],mod),tr[o].x.a[0][1]=1ll*a[l]*a[l]%mod,tr[o].x.a[0][2]=1,void();
		int mid=(l+r)>>1;
		build(l,mid,o<<1),build(mid+1,r,o<<1|1);
		pushup(o);
	}
	void update(int l,int r,int o,int x,int y,mat k){
		if(l>=x&&r<=y)return reset(o,k);
		pushdown(o);
		int mid=(l+r)>>1;
		if(x<=mid)update(l,mid,o<<1,x,y,k);
		if(y>mid)update(mid+1,r,o<<1|1,x,y,k);
		pushup(o);
	}
	int query(int l,int r,int o,int x,int y){
		if(r<x||l>y)return 0;
		if(l>=x&&r<=y)return tr[o].x.a[0][3];
		pushdown(o);
		int mid=(l+r)>>1;
		return Mod(query(l,mid,o<<1,x,y),query(mid+1,r,o<<1|1,x,y));
	}
}T;
void Yorushika(){
	scanf("%d%d%d",&n,&m,&q),m++;
	rep(i,1,n)scanf("%d",&a[i]);
	rep(i,2,m)scanf("%d%d%d",&b[i].l,&b[i].r,&b[i].x);
	rep(i,1,q){
		int l,r,x,y;scanf("%d%d%d%d",&l,&r,&x,&y);
		g[x].eb((Node){l,r,-1,i}),g[y+1].eb((Node){l,r,1,i});
	}
	T.build(1,n,1),b[1]={1,1,0};
	SGT::mat x;x.reset();
	rep(i,1,m){
		SGT::mat tmp;int x=b[i].x;
		tmp.a[0][0]=1,tmp.a[0][1]=Mod(2ll*x%mod,mod);
		tmp.a[1][1]=1;
		tmp.a[2][0]=Mod(x,mod),tmp.a[2][1]=1ll*x*x%mod,tmp.a[2][2]=1;
		tmp.a[3][3]=1;
		T.update(1,n,1,b[i].l,b[i].r,tmp);
		tmp.reset(),tmp.a[1][3]=1;
		T.reset(1,tmp);
		for(Node j:g[i])ans[j.id]=Mod(ans[j.id],Mod(mod,T.query(1,n,1,j.l,j.r)*j.s));
	}
	rep(i,1,q)printf("%d\n",ans[i]);
}
signed main(){
	int t=1;
	//	scanf("%d",&t);
	while(t--)
		Yorushika();
}
posted @   yinhee  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示