【题目泛做】道路(矩阵快速幂)(二项式展开)

简要题意:

一张有向图,求 u − > v u->v u>v所有长度不超过 k k k 的路径的长度 T T T 次方之和。

n ≤ 50 , T ≤ 50 , k ≤ 1 e 9 n\leq 50,T\leq 50,k\leq 1e9 n50,T50,k1e9


题解:

暴力的思路是矩阵 A l A_l Al 表示长度为 l l l u − > v u->v u>v 路径有多少条,然后矩阵乘法。

正解也非常傻逼,由于顺序无关,所以当处理完一部分之后,只需要考虑后面加上来的情况。


代码:

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

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

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<0?a-b+mod:a-b;}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);}

cs int N=50;

int n,k,q,T;
int C[N+1][N+1];

struct mat{
	int a[N][N];mat(){}
	int* operator[](int o){return a[o];}
	cs int* operator[](int o)cs{return a[o];}
	mat& operator=(cs mat &B){
		for(int re i=0;i<n;++i)
			memcpy(a[i],B[i],sizeof(int)*n);
		return *this;
	}
	mat& operator+=(cs mat &B){
		for(int re i=0;i<n;++i)
			for(int re j=0;j<n;++j)
				Inc(a[i][j],B[i][j]);
		return *this;
	}
	friend mat& operator*(cs mat &A,cs mat &B){
		static mat C;
		for(int re i=0;i<n;++i)
			memset(C[i],0,sizeof(int)*n);
		for(int re i=0;i<n;++i)
		for(int re j=0;j<n;++j)if(A[i][j])
		for(int re k=0;k<n;++k)
			Inc(C[i][k],mul(A[i][j],B[j][k]));
		return C;
	}mat& operator*=(cs mat &B){return *this=*this*B;}
	mat& operator*(int vl)cs{
		static mat C;
		for(int re i=0;i<n;++i)
			for(int re j=0;j<n;++j)
				C[i][j]=mul(a[i][j],vl);
		return C;
	}
};

struct arr{
	mat A[N+1],B;int L;arr(){}
	mat& operator[](int o){return A[o];}
	cs mat& operator[](int o)cs{return A[o];}
	arr& operator*=(cs arr &rhs){
		static int pw[N+1];
		for(int re i=pw[0]=1;i<=T;++i)
			pw[i]=mul(pw[i-1],L);
		for(int re i=T;~i;--i){
			static mat sm;
			for(int re i=0;i<n;++i)
				memset(sm[i],0,sizeof(int)*n);
			for(int re j=0;j<=i;++j)
				sm+=rhs[i-j]*mul(C[i][j],pw[j]);
			A[i]+=B*sm;
		}B*=rhs.B,L+=rhs.L;
		return *this;
	}
}A,B;

void Main(){
	scanf("%d%d%d%d",&n,&k,&q,&T);--k;
	for(int re i=0;i<=T;++i){
		C[i][0]=1;
		for(int re j=1;j<=i;++j)
			C[i][j]=add(C[i-1][j-1],C[i-1][j]);
	}B.L=1;
	for(int re i=0;i<n;++i)A.B[i][i]=1;
	for(int re i=0;i<n;++i)
		for(int re j=0;j<n;++j)
			scanf("%d",&B.B[i][j]);
	for(int re i=0;i<=T;++i)B[i]=B.B;
	while(k){
		if(k&1)A*=B;
		if(k>>=1)B*=B;
	}while(q--){
		int u,v;
		scanf("%d%d",&u,&v);
		cout<<A[T][u-1][v-1]<<"\n";
	}
}

inline void file(){
#ifdef zxyoi
	freopen("road.in","r",stdin);
#endif
}signed main(){file();Main();return 0;}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值