2019.03.26【洛谷P5107】能量采集(BSGS加速矩阵乘法)

本文介绍了一种使用矩阵快速幂优化计算大规模矩阵乘法的方法,并通过分块技术进一步减少时间复杂度。利用BSGS思想将矩阵分为若干块进行计算,通过预处理得到不同块之间的转移矩阵,最终实现高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门


解析:

很显然, t t t轮之后点 i i i对点 j j j产生贡献的比例是可以用矩阵乘法算出的。

计算出转移矩阵之后只需要用一个 1 × n 1\times n 1×n的原始状态矩阵乘上去就行了,所以现在考虑怎么优化求出转移矩阵的时间复杂度。

一种想法是二进制拆分,但是复杂度还有一个 log ⁡ \log log感觉不是很卡得过去。。。

另外一种想法就是利用BSGS。

但是我们发现分 n \sqrt n n 块空间卡不过去。。。

那么就分成四份,按照 n 4 \sqrt[4]n 4n 来算就行了。但是我为了方便,直接用的256块一步。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re char c;
		while(!isdigit(c=gc()));re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
}
using namespace IO;

using std::cout;
using std::cerr;
using std::swap;

cs int mod=998244353;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(int a,int b){return a<b?a-b+mod:a-b;}
inline int mul(int a,int b){return (ll)a*b%mod;}
inline int quickpow(int a,int b,int res=1){
	while(b){
		if(b&1)res=mul(res,a);
		a=mul(a,a);
		b>>=1;
	}
	return res;
}

cs int SIZE=50,N=50;
struct matrix{
	int a[SIZE][SIZE];
	int r,c;
	matrix(){memset(a,0,sizeof a);}
	
	int *operator[](int offset){return a[offset];}
	cs int *operator[](int offset)cs{return a[offset];}
	
	friend matrix operator*(cs matrix &A,cs matrix &B){
		matrix C;C.r=A.r,C.c=B.c;
		for(int re i=0;i<A.r;++i)
		for(int re j=0;j<A.c;++j)if(A[i][j])
		for(int re k=0;k<B.c;++k)C[i][k]=add(C[i][k],mul(A[i][j],B[j][k]));
		return C;
	}
	
}A0[256],A1[256],A2[256],A3[256],ori,res;

int n,m,q,deg[N],inv[(N+1)*N];

inline void init(){
	n=getint();m=getint();q=getint();
	inv[0]=inv[1]=1;
	for(int re i=2;i<N*(N+1);++i)inv[i]=mul(inv[mod%i],mod-mod/i);
	for(int re i=0;i<n;++i)ori[0][i]=getint(),A0[0][i][i]=deg[i]=1;
	ori.r=1,ori.c=A0[0].r=A0[0].c=n;
	A0[1]=A0[0];
	while(m--){
		int x=getint()-1,y=getint()-1;
		++A0[1][x][y];
		++deg[x];
	}
	for(int re i=0;i<n;++i)
	for(int re j=0;j<n;++j)A0[1][i][j]=mul(A0[1][i][j],inv[deg[i]]);
	for(int re i=2;i<256;++i)A0[i]=A0[i-1]*A0[1];
	A1[0]=A0[0],A1[1]=A0[255]*A0[1];
	for(int re i=2;i<256;++i)A1[i]=A1[i-1]*A1[1];
	A2[0]=A1[0],A2[1]=A1[255]*A1[1];
	for(int re i=2;i<256;++i)A2[i]=A2[i-1]*A2[1];
	A3[0]=A2[0],A3[1]=A2[255]*A2[1];
	for(int re i=2;i<256;++i)A3[i]=A3[i-1]*A3[1];
}

signed main(){
	init();
	std::ios::sync_with_stdio(false);
	while(q--){
		m=getint();
		res=ori*A0[m&255]*A1[m>>8&255]*A2[m>>16&255]*A3[m>>24&255];
		int ans=0;
		for(int re i=0;i<n;++i)ans^=res[0][i];
		cout<<ans%mod<<"\n";
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值