【HNOI2019】白兔之舞(单位根反演)(矩阵快速幂)(MTT)(循环卷积优化常数)

传送门


题解:

说个笑话,这道题很休闲的

真正的好题,会让你算t等于所有情况的答案

考虑走 i i i步的答案,显然就是 A i [ x ] [ y ] ( L i ) A^i[x][y]{L\choose i} Ai[x][y](iL),其中 A i A^i Ai表示原矩阵的 i i i次幂,其中 [ x ] [ y ] [x][y] [x][y]表示取矩阵中间的某个元素, ( L i ) {L\choose i} (iL)是因为需要在中间选择 i i i个地方落脚。

那么考虑模 k k k为某个定值的方案,显然是单位根反演,为了方便书写,以下矩阵中某个元素求和部分省略,以矩阵求和代替。

a n s t = ∑ i % k = t A i ( L i ) = ∑ i = 0 L [ k ∣ i − t ] A i ( L i ) = ∑ i = 0 L 1 k ∑ j = 0 k − 1 ω k j ( i − t ) A i ( L i ) = 1 k ∑ j = 0 k − 1 ω k − j t ∑ i = 0 L ( ω k j A ) i ( L i ) = 1 k ∑ j = 0 k − 1 ω k − j t ( ω k j A + I ) L \begin{aligned} ans_t=&\sum_{i\%k=t}A^i{L\choose i}\\ =&\sum_{i=0}^L[k\mid i-t]A^i{L\choose i}\\ =&\sum_{i=0}^L\frac{1}{k}\sum_{j=0}^{k-1}\omega_k^{j(i-t)}A^i{L\choose i}\\ =&\frac{1}{k}\sum_{j=0}^{k-1}\omega_k^{-jt}\sum_{i=0}^L(\omega_k^jA)^i{L\choose i}\\ =&\frac{1}{k}\sum_{j=0}^{k-1}\omega_k^{-jt}(\omega_k^jA+I)^L \end{aligned} anst=====i%k=tAi(iL)i=0L[kit]Ai(iL)i=0Lk1j=0k1ωkj(it)Ai(iL)k1j=0k1ωkjti=0L(ωkjA)i(iL)k1j=0k1ωkjt(ωkjA+I)L

显然后面的显然后面的矩阵可以直接快速幂,并且我们只会用到一个位置上的值,设 a j = ( ω k j A + I ) L [ x ] [ y ] a_j=(\omega_k^jA+I)^L[x][y] aj=(ωkjA+I)L[x][y]

继续化简:

a n s t = 1 k ∑ j = 0 k − 1 ω k − j t a j \begin{aligned} ans_t=&\frac{1}{k}\sum_{j=0}^{k-1}\omega_k^{-jt}a_j \end{aligned} anst=k1j=0k1ωkjtaj

这个已经可以 O ( k 2 ) O(k^2) O(k2)愉快搞掉了。多点求值是被卡了的。

现在需要考虑对于所有 t t t算这个东西。

显然我们需要把 t t t从指数上面和 j j j搞分开。

考虑BlueStein里面构造单位根的方式 i j = i 2 + j 2 − ( i − j ) 2 2 ij=\frac{i^2+j^2-(i-j)^2}{2} ij=2i2+j2(ij)2,然而我们并不敢保证 i 2 / 2 i^2/2 i2/2一定是一个整数。

似乎陷入了僵局。

然鹅,我们考虑这个式子 − j t = ( j 2 ) + ( t 2 ) − ( j + t 2 ) -jt={j\choose 2}+{t\choose 2}-{j+t\choose 2} jt=(2j)+(2t)(2j+t)

好的,组合数永远是整数, 喜闻乐见,我们赢了

所以上面这个东西:

a n s t = ( t 2 ) k ∑ i = 0 k − 1 ω ( j 2 ) a j ⋅ ω k − ( j + t 2 ) ans_t=\frac{t\choose 2}{k}\sum_{i=0}^{k-1}\omega^{j\choose 2}a_j\cdot \omega_k^{-{j+t\choose 2}} anst=k(2t)i=0k1ω(2j)ajωk(2j+t)

好的,翻转,构造卷积,我们赢了

考虑一个不重要的常数优化。

由于是一个 k k k次多项式和一个 2 ∗ k − 2 2*k-2 2k2次多项式做卷积。

只会用到结果的 k k k 2 k − 1 2k-1 2k1次,可以把卷积数组开到 2 k 2k 2k,循环卷积到前面的值可以直接丢掉。

然后写一个悠闲的MTT,一发入魂

细节不是很多,但是我矩阵乘法写挂了,叫你循环展开。。。


代码:

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

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

int n,k,L,x,y,mod;
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 SIZE=1<<17|1;
int ome[SIZE],iome[SIZE],C[SIZE],gr;


inline void get_gr(){
	int phi=mod-1;std::vector<int> p;
	for(int re i=2;i*i<=phi;++i)if(phi%i==0){
		p.push_back(i);
		while(phi%i==0)phi/=i;
	}
	if(phi>1)p.push_back(phi);phi=mod-1;
	for(gr=2;;++gr){
		bool flag=true;
		for(int t:p)if(power(gr,phi/t)==1){
			flag=false;break;
		}
		if(flag)break;
	}
}

struct matrix{
	int data[3][3];
	matrix(){}
	cs int *operator[](int of)cs{return data[of];}
	int *operator[](int of){return data[of];}
};
#define p mod
#define int64 ll
inline matrix operator+(cs matrix &A,cs matrix &B){
	matrix C;
	switch(n){
		case 3:
			C[0][0]=add(A[0][0],B[0][0]);C[0][1]=add(A[0][1],B[0][1]);C[0][2]=add(A[0][2],B[0][2]);
			C[1][0]=add(A[1][0],B[1][0]);C[1][1]=add(A[1][1],B[1][1]);C[1][2]=add(A[1][2],B[1][2]);
			C[2][0]=add(A[2][0],B[2][0]);C[2][1]=add(A[2][1],B[2][1]);C[2][2]=add(A[2][2],B[2][2]);
			break;
		case 2:
			C[0][0]=add(A[0][0],B[0][0]);C[0][1]=add(A[0][1],B[0][1]);
			C[1][0]=add(A[1][0],B[1][0]);C[1][1]=add(A[1][1],B[1][1]);
			break;
		case 1:
			C[0][0]=add(A[0][0],B[0][0]);
	}
	return C;
}

inline matrix operator*(cs matrix &A,int b){
	matrix C;
	switch(n){
		case 3:
			C[0][0]=mul(A[0][0],b);C[0][1]=mul(A[0][1],b);C[0][2]=mul(A[0][2],b);
			C[1][0]=mul(A[1][0],b);C[1][1]=mul(A[1][1],b);C[1][2]=mul(A[1][2],b);
			C[2][0]=mul(A[2][0],b);C[2][1]=mul(A[2][1],b);C[2][2]=mul(A[2][2],b);
			break;
		case 2:
			C[0][0]=mul(A[0][0],b);C[0][1]=mul(A[0][1],b);
			C[1][0]=mul(A[1][0],b);C[1][1]=mul(A[1][1],b);
			break;
		case 1:
			C[0][0]=mul(A[0][0],b);
	}
	return C;
}

inline matrix operator*(cs matrix &A,cs matrix &B){
	matrix C;
	switch(n){
		case 3:
			C[0][0]=((ll)A[0][0]*B[0][0]+(ll)A[0][1]*B[1][0]+(ll)A[0][2]*B[2][0])%mod;
			C[0][1]=((ll)A[0][0]*B[0][1]+(ll)A[0][1]*B[1][1]+(ll)A[0][2]*B[2][1])%mod;
			C[0][2]=((ll)A[0][0]*B[0][2]+(ll)A[0][1]*B[1][2]+(ll)A[0][2]*B[2][2])%mod;
			C[1][0]=((ll)A[1][0]*B[0][0]+(ll)A[1][1]*B[1][0]+(ll)A[1][2]*B[2][0])%mod;
			C[1][1]=((ll)A[1][0]*B[0][1]+(ll)A[1][1]*B[1][1]+(ll)A[1][2]*B[2][1])%mod;
			C[1][2]=((ll)A[1][0]*B[0][2]+(ll)A[1][1]*B[1][2]+(ll)A[1][2]*B[2][2])%mod;
			C[2][0]=((ll)A[2][0]*B[0][0]+(ll)A[2][1]*B[1][0]+(ll)A[2][2]*B[2][0])%mod;
			C[2][1]=((ll)A[2][0]*B[0][1]+(ll)A[2][1]*B[1][1]+(ll)A[2][2]*B[2][1])%mod;
			C[2][2]=((ll)A[2][0]*B[0][2]+(ll)A[2][1]*B[1][2]+(ll)A[2][2]*B[2][2])%mod;
			break;
		case 2:
			C[0][0]=((ll)A[0][0]*B[0][0]+(ll)A[0][1]*B[1][0])%mod;
			C[0][1]=((ll)A[0][0]*B[0][1]+(ll)A[0][1]*B[1][1])%mod;
			C[1][0]=((ll)A[1][0]*B[0][0]+(ll)A[1][1]*B[1][0])%mod;
			C[1][1]=((ll)A[1][0]*B[0][1]+(ll)A[1][1]*B[1][1])%mod;
			break;
		case 1:
			C[0][0]=(ll)A[0][0]*B[0][0]%mod;
	}
	return C;
}

inline int power(matrix A,int b,int r,int c){
	int res[3]={};res[r]=1;
	while(b){
		if(b&1){
			int tp[3]={};
			switch(n){
				case 3:
					tp[0]=((ll)res[0]*A[0][0]+(ll)res[1]*A[1][0]+(ll)res[2]*A[2][0])%mod;
					tp[1]=((ll)res[0]*A[0][1]+(ll)res[1]*A[1][1]+(ll)res[2]*A[2][1])%mod;
					tp[2]=((ll)res[0]*A[0][2]+(ll)res[1]*A[1][2]+(ll)res[2]*A[2][2])%mod;
					res[0]=tp[0];res[1]=tp[1];res[2]=tp[2];
					break;
				case 2:
					tp[0]=((ll)res[0]*A[0][0]+(ll)res[1]*A[1][0])%mod;
					tp[1]=((ll)res[0]*A[0][1]+(ll)res[1]*A[1][1])%mod;
					res[0]=tp[0];res[1]=tp[1];
					break;
				case 1:
					res[0]=(ll)res[0]*A[0][0]%mod;
			}
		}
		A=A*A;b>>=1;
	}
	return res[c];
}

matrix A,I;

namespace MTT{
	cs double PI=acos(-1);
	struct cp{
		double x,y;
		cp(){}
		cp(double _x,double _y):x(_x),y(_y){}
		cp conj()cs{return cp(x,-y);}
		friend cp operator+(cs cp &a,cs cp &b){return cp(a.x+b.x,a.y+b.y);}
		friend cp operator-(cs cp &a,cs cp &b){return cp(a.x-b.x,a.y-b.y);}
		friend cp operator*(cs cp &a,cs cp &b){return cp(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
		friend cp operator/(cs cp &a,double b){return cp(a.x/b,a.y/b);}
	};
	cp w[SIZE];int r[SIZE];
	inline void init_rev(int l){
		cp wn(cos(2*PI/l),sin(2*PI/l));
		for(int re i=0;i<l/2;++i)
		w[l/2+i]=(i&31)?w[l/2+i-1]*wn:cp(cos(2*PI*i/l),sin(2*PI*i/l));
		for(int re i=l/2-1;i;--i)w[i]=w[i<<1];
		for(int re i=1;i<l;++i)r[i]=r[i>>1]>>1|((i&1)?l>>1:0);
	}
	inline void DFT(cp *A,int l){
		for(int re i=1;i<l;++i)if(i<r[i])std::swap(A[i],A[r[i]]);
		for(int re i=1;i<l;i<<=1)
		for(int re j=0;j<l;j+=i<<1)
		for(int re k=0;k<i;++k){
			cp t=A[j+k+i]*w[i+k];
			A[j+k+i]=A[j+k]-t;
			A[j+k]=A[j+k]+t;
		}
	}
	void mul(int *a,int *b,int len){
		static cp A[SIZE],B[SIZE],C[SIZE],D[SIZE];
		for(int re i=0;i<len;++i){
			A[i]=cp(a[i]&0x7fff,a[i]>>15);
			B[i]=cp(b[i]&0x7fff,b[i]>>15);
		} 
		init_rev(len),DFT(A,len),DFT(B,len);
		for(int re i=0;i<len;++i){
			int u=(len-i)&(len-1);
			C[i]=(A[i].conj()+A[u])*cp(0.5,0)*B[u];
			D[i]=(A[i].conj()-A[u])*cp(0,0.5)*B[u];
		}
		DFT(C,len),DFT(D,len);
		for(int re i=0;i<len;++i){
			ll x=C[i].x/len+0.5,y=(C[i].y+D[i].x)/len+0.5,z=D[i].y/len+0.5;
			x%=mod,y%=mod,z%=mod;
			a[i]=(x+(y<<15)+(z<<30))%mod;
		}
	}
}

int a[SIZE],b[SIZE],c[SIZE];

signed main(){
#ifdef zxyoi
	freopen("dance.in","r",stdin);
#endif
	scanf("%d%d%d%d%d%d",&n,&k,&L,&x,&y,&mod);--x,--y;
	for(int re i=0;i<n;++i)
	for(int re j=0;j<n;++j)scanf("%d",&A[i][j]),I[i][j]=(i==j);
	get_gr();
	iome[0]=ome[0]=1;ome[1]=power(gr,(mod-1)/k);
	for(int re i=2;i<k;++i)ome[i]=mul(ome[i-1],ome[1]);
	for(int re i=1;i<k;++i)iome[i]=ome[k-i];
	for(int re i=1;i<=2*k-2;++i)C[i]=(ll)i*(i-1)/2%k;
	for(int re i=0;i<k;++i)c[i]=mul(power(A*ome[i]+I,L,x,y),ome[C[i]]);
	std::reverse(c,c+k+1);
	for(int re i=0;i<=2*k-2;++i)a[i]=iome[C[i]];
	int len=1;while(len<2*k)len<<=1;
	MTT::mul(a,c,len);
	int inv_k=power(k,mod-2);
	for(int re i=0;i<k;++i)cout<<mul(a[k+i],mul(inv_k,ome[C[i]]))<<"\n";
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值