【LOJ2463】「2018 集训队互测 Day 1」完美的旅行(线代黑科技)(牛顿恒等式)

传送门


题解:

这道题的原理部分请看这篇题解:https://blog.csdn.net/zxyoi_dreamer/article/details/100160980

上面这篇题解里面的记号全部沿用。

这里主要讲一下这道题怎么用线代黑科技优化常数。

如果一个数列是线性递推数列,设其OGF为 G ( x ) G(x) G(x),则存在两个有限多项式 P ( x ) , Q ( x ) P(x),Q(x) P(x),Q(x),使得 G ( x ) = P ( x ) Q ( x ) G(x)=\frac{P(x)}{Q(x)} G(x)=Q(x)P(x),且 Q ( x ) Q(x) Q(x)常数项为1。

这道题里面我们可以发现对于 G i ( x ) G_i(x) Gi(x),其常数项为 0 0 0,则 F i ( x ) = 1 1 − G i ( x ) F_i(x)=\frac{1}{1-G_i(x)} Fi(x)=1Gi(x)1,所以 f ( i , k ) f(i,k) f(i,k)也是线性递推数列,并且其线性递推式可以直接从 g ( i , k ) g(i,k) g(i,k)的线性递推式得到。具体的推导过程我就咕咕咕了。

那么现在问题就是怎么求 g ( i , k ) g(i,k) g(i,k)的递推式,也就是转移矩阵 A \mathbf A A的特征多项式。

可以用 O ( n 4 ) O(n^4) O(n4)求出前几项后用BM算法。

但是求一般矩阵特征多项式的算法复杂度就是 O ( n 4 ) O(n^4) O(n4)。。。

下面介绍两种方式求矩阵的特征多项式,记为 c h a r ( A ) = λ n − ∑ i = 0 n − 1 c i λ i \mathrm{char}(\mathbf A)=\lambda^n-\sum_{i=0}^{n-1}c_i\lambda^i char(A)=λni=0n1ciλi

1.根据定义 c h a r ( A ) = d e t ( λ I − A ) \mathrm{char}({\mathbf A})=\mathrm {det}(\lambda \mathbf I-\mathbf A) char(A)=det(λIA),代入 λ = 1 , 2 , ⋯ n \lambda=1,2,\cdots n λ=1,2,n,然后高斯消元解行列式和多项式即可,或者代入单位根,之后直接用IDFT可以得到原多项式。
2.考虑利用牛顿恒等式,假设 n n n个特征根为 λ 1 , λ 2 ⋯ λ n \lambda_1,\lambda_2\cdots\lambda_n λ1,λ2λn,有 t r ( A k ) = ∑ i = 1 n λ i k \mathrm tr(\mathbf A^k)=\sum_{i=1}^n\lambda_i^k tr(Ak)=i=1nλik,然后利用牛顿恒等式还原回去就行了。

关于牛顿恒等式:

假设 n n n次多项式 A ( x ) = ∑ i = 0 n a i x i A(x)=\sum_{i=0}^na_ix^i A(x)=i=0naixi n n n个根分别为 x 1 , x 2 ⋯ x n x_1,x_2\cdots x_n x1,x2xn,设 b i = a n − i b_i=a_{n-i} bi=ani,设 S k = ∑ i = 1 n x i k S_k=\sum_{i=1}^nx_i^k Sk=i=1nxik,则有 ∀ k ∈ N ∗ , ∑ i = 1 k S i b k − i + k ⋅ b k = 0 \forall k\in \mathbb N^*,\sum_{i=1}^kS_ib_{k-i}+k\cdot b_k=0 kN,i=1kSibki+kbk=0

在知道 S i S_i Si之后可以愉快地反解系数了。


代码:

#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<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	template<typename T>
	inline T get(){
		char c;
		while(!isdigit(c=gc()));T num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int gi(){return get<int>();}
}
using namespace IO;

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

cs int mod=998244353;
inline int add(int a,int b){a+=b-mod;return a+(a>>31&mod);}
inline int dec(int a,int b){a-=b;return a+(a>>31&mod);}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline int power(int a,int b,int res=1){
	for(;b;b>>=1,a=mul(a,a))(b&1)&&(res=mul(res,a));
	return res;
}
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=77,M=2e4+7;

int n,m;
int A[N][N];
int way[N][N][N];
int tr[N],coef[N],tp[N];
int ans[N][M];

signed main(){
#ifdef zxyoi
	freopen("travel.in","r",stdin);
#endif
	n=gi(),m=gi();
	for(int re i=0;i<n;++i){
		way[0][i][i]=1;
		for(int re j=0;j<n;++j)A[i][j]=gi();
	}
	for(int re t=1;t<=n;++t)
	for(int re i=0;i<n;++i)
	for(int re j=0;j<n;++j)if(int v=way[t-1][i][j])
	for(int re k=0;k<n;++k)Inc(way[t][i][k],mul(v,A[j][k]));
	for(int re i=1;i<=n;++i)
	for(int re j=0;j<n;++j)Inc(tr[i],way[i][j][j]);
	coef[0]=1;
	for(int re i=1;i<=n;++i){
		for(int re j=0;j<i;++j)
		coef[i]=dec(mul(coef[j],tr[i-j]),coef[i]);
		Mul(coef[i],power(i,mod-2));
	}
	for(int re i=1;i<=n;i+=2)coef[i]=dec(0,coef[i]);
	for(int re t=1;t<=n;++t){
		memset(tp,0,sizeof(int)*n);
		for(int re i=0;i<n;++i)
		for(int re j=0;j<n;++j)Inc(tp[i&j],way[t][i][j]);
		for(int re i=0;i<n;++i)
		for(int re j=0;j<n;++j)if((i|j)==j)Inc(ans[i][t],tp[j]);
	}
	for(int re t=0;t<n;++t){
		memcpy(tp,coef,sizeof(int)*(n+1));
		for(int re i=0;i<=n;++i)
		for(int re j=0;j<=i;++j)Dec(tp[i],mul(coef[j],ans[t][i-j]));
		for(int re i=0;i<=m;++i){
			ans[t][i]=i<=n?coef[i]:0;
			for(int re j=1,lj=std::min(i,n);j<=lj;++j)
			Dec(ans[t][i],mul(ans[t][i-j],tp[j]));
		}
	}
	int res=0;
	for(int re i=n-1;~i;--i){
		for(int re j=n-1;j>i;--j)if((i|j)==j)
		for(int re s=1;s<=m;++s)Dec(ans[i][s],ans[j][s]);
		for(int re s=1;s<=m;++s)res^=ans[i][s];
	}
	cout<<res<<"\n";
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值