2018.01.02【CQOI2018】【BZOJ5298】【洛谷P4456】交错序列(矩阵快速幂)(轻微卡常)

BZOJ传送门

洛谷传送门


解析:

本来还可以继续把矩阵乘法一分为八卡卡常数的,今天就算了,CQOI2018真的做的我没心情卡了。。。

不知道出题人是哪根筋搭错了,5道版题还有两道卡常。。。

我只能说,这届CQOI出题人的水平估计都没有选手高(此话不负任何法律或人身责任(滑稽)

思路:

考虑原来要求的式子: x a y b x^ay^b xayb怎么化简。

x a y b = ( n − x ) a y b = ∑ i = 0 a C a i ( − 1 ) a − i n i y a + b − i \begin{aligned} x^ay^b&=(n-x)^ay^b\\ &=\sum_{i=0}^{a}C_a^i(-1)^{a-i}n^iy^{a+b-i} \end{aligned} xayb=(nx)ayb=i=0aCai(1)ainiya+bi

显然前面这个东西 ∑ i = 0 a C a i ( − 1 ) a − i n i \sum_{i=0}^{a}C_a^i(-1)^{a-i}n^i i=0aCai(1)aini可以很轻易的算出。

那么我们要求的就是所有长度为 n n n的交错序列中, y a + b − i y^{a+b-i} ya+bi的和。

考虑DP,设 f [ k ] [ i ] [ 1 / 0 ] f[k][i][1/0] f[k][i][1/0]表示所有长度为 k k k的,结尾是 0 / 1 0/1 0/1的交错序列中, 1 1 1的个数,即 y y y i i i次方之和。

显然,我们可以得到如下转移:
f [ k ] [ i ] [ 0 ] = f [ k − 1 ] [ i ] [ 0 ] + f [ k − 1 ] [ i ] [ 1 ] f [ k ] [ i ] [ 1 ] = ∑ j = 0 i C i j f [ k − 1 ] [ j ] [ 0 ] \begin{aligned} f[k][i][0]&=f[k-1][i][0]+f[k-1][i][1] \\ f[k][i][1]&=\sum_{j=0}^{i}C_i^jf[k-1][j][0] \end{aligned} f[k][i][0]f[k][i][1]=f[k1][i][0]+f[k1][i][1]=j=0iCijf[k1][j][0]

其中第二个式子由二项式定理得到,考虑所有 y y y加一之后的二项式展开。

这个显然是矩阵乘法的形式,构造矩阵然后快速幂转移就好了。

卡常:

当然你要是认为这道题就完了也可以,慢慢卡常吧。

首先上矩阵快速幂卡常基础操作:除去 0 0 0,优化循环结构。

然而这道题还有一个令人蛋疼的性质。

大矩阵是由四个小下三角矩阵拼起来的。。

所以乘法我们只需要对于每个下三角矩阵做一遍就行了。

当然,你也可以将矩阵一分为八,我已经不想写了。。。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define pc put_char
#define cs const

cs int N=190;
int mod;
inline int add(int a,int b){
	a+=b;return a>=mod?a-mod:a;
}

int siz1,siz2;
struct matrix{
	int a[N][N];
	matrix(){memset(a,0,sizeof a);}
	inline friend matrix operator*(cs matrix &A,cs matrix &B){
        matrix C;
		for(int re i=0;i<siz1;++i)
		for(int re j=0;j<siz2;++j)if(A.a[i][j])
		for(int re k=0;k<=i;++k)
		C.a[i][k]=add(C.a[i][k],1ll*A.a[i][j]*B.a[j][k]%mod);
		for(int re i=0;i<siz1;++i)
		for(int re j=0;j<siz2;++j)if(A.a[i][j])
		for(int re k=siz1;k<=siz1+i;++k)
		C.a[i][k]=add(C.a[i][k],1ll*A.a[i][j]*B.a[j][k]%mod);
		for(int re i=siz1;i<siz2;++i)
		for(int re j=0;j<siz2;++j)if(A.a[i][j])
		for(int re k=0;k<=i-siz1;++k)
		C.a[i][k]=add(C.a[i][k],1ll*A.a[i][j]*B.a[j][k]%mod);
		for(int re i=siz1;i<siz2;++i)
		for(int re j=0;j<siz2;++j)if(A.a[i][j])
		for(int re k=siz1;k<=i;++k)
		C.a[i][k]=add(C.a[i][k],1ll*A.a[i][j]*B.a[j][k]%mod);
		return C;
	}	
}mat;

inline matrix quickpow(matrix a,int b){
	matrix res;
	for(int re i=0;i<siz2;++i)res.a[i][i]=1;
	for(;b;b>>=1,a=a*a)if(b&1)res=res*a;
	return res;
}

int fac[100],inv[100],ifac[100];
inline int C(int n,int m){
	return (ll)fac[n]*ifac[n-m]%mod*ifac[m]%mod;
}
 
int n,a,b,ans;
signed main(){
	scanf("%d%d%d%d",&n,&a,&b,&mod);
	fac[0]=ifac[0]=fac[1]=ifac[1]=inv[1]=inv[0]=1;
	for(int re i=2;i<=a+b;++i){
		fac[i]=(ll)i*fac[i-1]%mod;
		inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
		ifac[i]=(ll)ifac[i-1]*inv[i]%mod;
	}
	siz1=a+b+1,siz2=siz1<<1;
	for(int re i=0;i<siz1;++i){
		mat.a[i][i]=mat.a[i][siz1+i]=1;
		for(int re j=0;j<=i;++j)mat.a[siz1+i][j]=C(i,j);
	}
	mat=quickpow(mat,n);
	int pown=1;
	for(int re i=0;i<=a;++i){
		ans+=(((a-i)&1)?-1ll:1ll)*C(a,i)*pown%mod*add(mat.a[a+b-i][0],mat.a[a+b-i+siz1][0])%mod;
		ans%=mod;
		pown=(ll)pown*n%mod;
	}
	cout<<(ans%mod+mod)%mod;
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值