题解:B3646 数列前缀和 3

背景

没有人写线段树?那我来写一发。

前置

矩阵乘法,单位元矩阵,线段树。

分析

这道题目给了我们一些矩阵,让我们多次求出一个给定区间内所有矩阵的乘积。

这道题当然可以直接用矩阵前缀积来做,但是因为要取模,所以需要求出矩阵逆,这样问题就变得复杂了。怎么办呢?线段树!

我们在线段树的每个叶子节点上面存一个矩阵,其余的存储区间矩阵积。最开始把所有的矩阵存进去,然后每次查询的时候按照题目要求异或即可。

需要注意的是,线段树查询操作累乘答案的时候,答案的初值应为单位元矩阵,否则会影响答案。

其余的就是一些常见的小细节了,放代码。

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int maxn=1e6+10;
const int mod=1145141;
int n,Q,ans=0,k;
struct no
{
	int num[4][4];
	int* operator [] (int i) 
	{
		return num[i]; 
	}
	no()
	{
		memset(num,0,sizeof num);
		for(int i=1;i<=k;i++)
		{
			num[i][i]=1;
		}
	}
	inline void _read()
	{
		for(int i=1;i<=k;i++)
		{
			for(int j=1;j<=k;j++)
			{
				num[i][j]=read();
			}
		}
	}
	inline void clear()
	{
		memset(num,0,sizeof num);
	}
	inline friend no operator * (no aa,no b)
	{
		no c;
		c.clear();
		for(int i=1;i<=k;i++)
		{
			for(int j=1;j<=k;j++)
			{
				for(int q=1;q<=k;q++)
				{
					c[i][j]=(c[i][j]+aa[i][q]*b[q][j]%mod)%mod;
				}
			}
		}
		return c;
	}
	inline int xor_()
	{
		int res=0;
		for(int i=1;i<=k;i++)
		{
			for(int j=1;j<=k;j++)
			{
				res=res^num[i][j];
			}
		}
		return res;
	}
}a[maxn];
struct Segment_Tree
{
	int l,r;
	no d;
}t[2001000];
void build(int p,int l,int r)
{
	t[p].l=l,t[p].r=r;
	if(l==r)
	{
		t[p].d=a[l];
		return ;
	}
	int mid=(l+r)>>1;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
	t[p].d=t[p*2].d*t[p*2+1].d;
}
no ask(int p,int l,int r)
{
	if(t[p].l>=l&&t[p].r<=r)return t[p].d;
	int mid=(t[p].l+t[p].r)>>1;
	no ans;
	if(mid>=l)ans=ans*ask(p*2,l,r);
	if(mid<r)ans=ans*ask(p*2+1,l,r);
	return ans;
}
signed main()
{
	cin>>n>>k>>Q;
	for(int i=1;i<=n;i++)
	{
		a[i]._read();
	}
	build(1,1,n);
	while(Q--)
	{
		int l=read(),r=read();
		no z=ask(1,l,r);
		ans=ans^z.xor_();
	}
	cout<<ans;
	return 0;
}

posted @ 2024-07-17 17:01  Redamancy_Lydic  阅读(6)  评论(0编辑  收藏  举报