题解:B3646 数列前缀和 3
B3646 数列前缀和 3
背景
不会矩阵求逆,遂选择线段树做法,做完发现 TLE 了,经过疯狂的常数优化后,通过了这道题。
前置知识
矩阵乘法,线段树。
分析
这道题目给了我们一些矩阵,让我们多次求出一个给定区间内所有矩阵的乘积。
这道题当然可以直接用矩阵前缀积来做,但是因为要取模,所以需要求出矩阵逆,这样问题就变得复杂了。怎么办呢?线段树!
我们在线段树的每个叶子节点上面存一个矩阵,其余的存储区间矩阵积。最开始把所有的矩阵存进去,然后每次查询的时候按照题目要求异或即可。
需要注意的是,线段树查询操作累乘答案的时候,答案的初值应为单位元矩阵,否则会影响答案。
还需要一个常数优化的技巧:循环展开。
循环展开,英文中称 (Loop unwinding 或 loop unrolling) ,是一种牺牲程序的尺寸来加快程序的执行速度的优化方法。
循环展开可以省去 for 循环每次的判断与修改操作,对于多次调用循环的程序,把循环展开可以有效卡常。
其余的就是一些常见的小细节了,放代码。
Code
#include<bits/stdc++.h>
using namespace std;
#define ll unsigned long long
#define ls (p<<1)
#define rs (p<<1|1)
inline int read()//快读
{
int s=0;char c=getchar();
for(;!isdigit(c);c=getchar());
for(;isdigit(c);c=getchar())
s=(s<<1)+(s<<3)+(c^48);
return s;
}
int const N=1e6+9;
int const mod=1145141;
int n,k,q,ans;
ll tmp[3][3];
struct matrix{
ll a[3][3];
inline void operator *=(const matrix &B)//矩阵乘法
{
memcpy(tmp,a,sizeof a);
if(k==3)//k=3时的循环展开
{
a[0][0]=(tmp[0][0]*B.a[0][0]+tmp[0][1]*B.a[1][0]+tmp[0][2]*B.a[2][0])%mod;
a[0][1]=(tmp[0][0]*B.a[0][1]+tmp[0][1]*B.a[1][1]+tmp[0][2]*B.a[2][1])%mod;
a[0][2]=(tmp[0][0]*B.a[0][2]+tmp[0][1]*B.a[1][2]+tmp[0][2]*B.a[2][2])%mod;
a[1][0]=(tmp[1][0]*B.a[0][0]+tmp[1][1]*B.a[1][0]+tmp[1][2]*B.a[2][0])%mod;
a[1][1]=(tmp[1][0]*B.a[0][1]+tmp[1][1]*B.a[1][1]+tmp[1][2]*B.a[2][1])%mod;
a[1][2]=(tmp[1][0]*B.a[0][2]+tmp[1][1]*B.a[1][2]+tmp[1][2]*B.a[2][2])%mod;
a[2][0]=(tmp[2][0]*B.a[0][0]+tmp[2][1]*B.a[1][0]+tmp[2][2]*B.a[2][0])%mod;
a[2][1]=(tmp[2][0]*B.a[0][1]+tmp[2][1]*B.a[1][1]+tmp[2][2]*B.a[2][1])%mod;
a[2][2]=(tmp[2][0]*B.a[0][2]+tmp[2][1]*B.a[1][2]+tmp[2][2]*B.a[2][2])%mod;
}else {//k=2
a[0][0]=(tmp[0][0]*B.a[0][0]+tmp[0][1]*B.a[1][0])%mod;
a[0][1]=(tmp[0][0]*B.a[0][1]+tmp[0][1]*B.a[1][1])%mod;
a[1][0]=(tmp[1][0]*B.a[0][0]+tmp[1][1]*B.a[1][0])%mod;
a[1][1]=(tmp[1][0]*B.a[0][1]+tmp[1][1]*B.a[1][1])%mod;
}
}
inline int xor_()//矩阵异或
{
int res=0;
if(k==3)//还是循环展开
{
res^=a[0][0];
res^=a[0][1];
res^=a[0][2];
res^=a[1][0];
res^=a[1][1];
res^=a[1][2];
res^=a[2][0];
res^=a[2][1];
res^=a[2][2];
}else{
res^=a[0][0];
res^=a[0][1];
res^=a[1][0];
res^=a[1][1];
}
return res;
}
}tr[N<<2],a[N];
void build(int l,int r,int p)//建树
{
if(l==r)
{
tr[p]=a[l];
return;
}
int mid=(l+r)>>1;
build(l,mid,ls);
build(mid+1,r,rs);
tr[p]=tr[ls];
tr[p]*=tr[rs];
}
matrix query(int nl,int nr,int l,int r,int p)//查询
{
if(nl<=l&&r<=nr)
return tr[p];
int mid=(l+r)>>1;
matrix res;memset(res.a,0,sizeof(res.a));
res.a[0][0]=res.a[1][1]=res.a[2][2]=1;
if(nl<=mid)res*=query(nl,nr,l,mid,ls);
if(nr>mid)res*=query(nl,nr,mid+1,r,rs);
return res;
}
signed main()
{
n=read();k=read();q=read();
for(int t=1;t<=n;t++)
{
if(k==3)
{
a[t].a[0][0]=read();
a[t].a[0][1]=read();
a[t].a[0][2]=read();
a[t].a[1][0]=read();
a[t].a[1][1]=read();
a[t].a[1][2]=read();
a[t].a[2][0]=read();
a[t].a[2][1]=read();
a[t].a[2][2]=read();
}else{
a[t].a[0][0]=read();
a[t].a[0][1]=read();
a[t].a[1][0]=read();
a[t].a[1][1]=read();
}
}
build(1,n,1);
int x,y;
matrix z;
while(q--)
{
x=read();y=read();
z=query(x,y,1,n,1);
ans^=z.xor_();
}
printf("%d",ans);
}
特别鸣谢
@lkwbian