BZOJ5298: [Cqoi2018]交错序列

BZOJ

题意

称一个仅由 0 , 1 0,1 0,1构成的序列为 “交错序列”,当且仅当序列中没有相邻的 1 ( 1( 1(可以有相邻的 0 ) 0) 0);定义一个交错序列的特征值为 x a ∗ y b x^a*y^b xayb,其中 x x x为系列中 0 0 0的个数, y y y为序列中 1 1 1的个数, a , b a,b a,b为给定的常数;求本质不同的长度 n n n的交错序列的特征值的和;

题解

考虑将特征值的式子用二项式定理展开:
x a ∗ y b x^a*y^b xayb ↓ ↓ ( n − y ) a ∗ y b (n-y)^a*y^b (ny)ayb ↓ ↓ ( ∑ i = 0 a C a i ∗ n a − i ∗ ( − y ) i ) ∗ y b (\sum_{i=0}^aC_a^i*n^{a-i}*(-y)^i)*y^b (i=0aCainai(y)i)yb ↓ ↓ ∑ i = 0 a ( − 1 ) i ∗ C a i ∗ n a − i ∗ y b + i \sum_{i=0}^a(-1)^i*C_a^i*n^{a-i}*y^{b+i} i=0a(1)iCainaiyb+i现在我们的问题就变成了求每个 y i ( 0 ≤ i ≤ a + b ) y^i(0\leq i\leq a+b) yi(0ia+b);
因为特征值的计算只跟 01 01 01的个数有关,所以我们只关心 01 01 01的个数,不关心那个序列到底长啥样,但是 n n n 1 0 8 10^8 108,如果要算出每种序列的个数再去继续处理的话其实并不好算,考虑直接定义一个状态 F [ i ] [ j ] [ 0 / 1 ] F[i][j][0/1] F[i][j][0/1]表示用 0 0 0 1 1 1去填满这个序列,填到第 i i i位时以 1 / 0 1/0 1/0结尾的所有的 y j y^j yj的和,也就是 F [ i ] [ j ] [ 0 / 1 ] = ∑ y = 0 n y j F[i][j][0/1]=\sum_{y=0}^ny^j F[i][j][0/1]=y=0nyj, y y y是上面展开的式子中的 y y y,也就是 1 1 1的个数;那么有:
F [ i ] [ j ] [ 0 ] = F [ i − 1 ] [ j ] [ 0 ] + F [ i − 1 ] [ j ] [ 1 ] F[i][j][0]=F[i-1][j][0]+F[i-1][j][1] F[i][j][0]=F[i1][j][0]+F[i1][j][1] F [ i ] [ j ] [ 1 ] = ∑ r = 0 j C j i ∗ F [ i − 1 ] [ j ] [ 0 ] F[i][j][1]=\sum_{r=0}^jC_j^i*F[i-1][j][0] F[i][j][1]=r=0jCjiF[i1][j][0] 1 1 1的转移同样可以通过二项式定理展开得到;
这恰好是一个线性递推,考虑用矩阵快速幂加速,最后带入展开的特征值式子计算即可;

#include<bits/stdc++.h>
#define Fst first
#define Snd second
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long LL;
typedef unsigned int UI;
typedef unsigned long long ULL;
template<typename T> inline void read(T& x) {
	char c = getchar();
	bool f = false;
	for (x = 0; !isdigit(c); c = getchar()) {
		if (c == '-') {
			f = true;
		}
	}
	for (; isdigit(c); c = getchar()) {
		x = x * 10 + c - '0';
	}
	if (f) {
		x = -x;
	}
}
template<typename T, typename... U> inline void read(T& x, U& ... y) {
	read(x), read(y...);
}
int n,P,LIM;
int C[200][200],POW[100];
struct Matrix {
	int M[182][182];
}A,B;
typedef Matrix Mt;
void ADD(int &a,int b) {
	a+=b; if(a>=P) a-=P;
}
Mt operator *(Mt a,Mt b) {
	Mt c;
	for(int i=0;i<LIM;++i)
		for(int j=0;j<LIM;++j)
			c.M[i][j]=0;
	for(int i=0;i<LIM;++i) {
		for(int k=0;k<LIM;++k) if(a.M[i][k]) {
			for(int j=0;j<LIM;++j) if(b.M[k][j]) {
				ADD(c.M[i][j],1ll*a.M[i][k]*b.M[k][j]%P);
			}
		}
	}
	return c;
}
Mt Pow(Mt a,int k) {
	Mt res;
	for(int i=0;i<LIM;++i)
		for(int j=0;j<LIM;++j)
			res.M[i][j]=i==j;
	for(int i=k;i;i>>=1) {
		if(i&1) res=res*a;
		a=a*a;
	}
	return res;
}
int main() {
	int a,b; read(n,a,b,P);
	LIM=a+b+1<<1;
	for(int i=0;i<=a+b;++i) {
		C[i][0]=C[i][i]=1;
		for(int j=1;j<i;++j)
			C[i][j]=(C[i-1][j-1]+C[i-1][j])%P;
	}
	A.M[0][0]=1;
	for(int i=0;i<LIM/2;++i) {
		B.M[i][i]=B.M[i+LIM/2][i]=1;
		for(int j=0;j<LIM/2;++j)
			B.M[i][j+LIM/2]=C[j][i];
	}
	A=A*Pow(B,n);
	POW[0]=1;
	for(int i=1;i<=a;++i) POW[i]=1ll*POW[i-1]*n%P;
	int ANS=0;
	for(int i=0;i<=a;++i) ANS=(ANS+1ll*(i&1?-1:1)*C[a][i]*POW[a-i]%P*((A.M[0][b+i]+A.M[0][b+i+LIM/2])%P)%P)%P;
	printf("%d\n",(ANS%P+P)%P);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值