1. 程式人生 > >bzoj 5298: [Cqoi2018]交錯序列

bzoj 5298: [Cqoi2018]交錯序列

solution bits 1出現的次數 play c++ con pri tdi isp

Description

我們稱一個僅由0、1構成的序列為"交錯序列",當且僅當序列中沒有相鄰的1(可以有相鄰的0)。例如,000,001
,101,都是交錯序列,而110則不是。對於一個長度為n的交錯序列,統計其中0和1出現的次數,分別記為x和y。
給定參數a、b,定義一個交錯序列的特征值為x^ay^b。註意這裏規定任何整數的0次冪都等於1(包括0^0=1)。
顯然長度為n的交錯序列可能有多個。我們想要知道,所有長度為n的交錯序列的特征值的和,除以m的余數。(m是
一個給定的質數)例如,全部長度為3的交錯串為:000、001、010、100、101。

Solution

看到 \(x^ay^b\) 容易想到要展開,然後就發現變成了

\[y^b*\sum_{i=0}^{a}(-1)^{a-i}*C_{a}^{i}*n^{i}*y^{a-i}\]
\[\sum_{i=0}^{a}(-1)^{a-i}*C_{a}^{i}*n^{i}*y^{a+b-i}\]
這樣就只需要統計 \(\sum y^{a+b-i}\)
\(f[i][j][0/1]\) 表示這一位填 \(0/1\) 的特征值的 \(j\) 次方之和
如果增加一個 \(1\),\(y^{j}\) 就要變成 \((y+1)^{j}\),二項式定理展開做個差
發現就是 \(\sum_{k=0}^{j-1}C_{j}^{k}*y^k\)
\(f[i][k]\) 就是 \(y^k\)

轉移系數就確定了,矩陣快速冪優化一下就好了
千萬不要像我一樣拆成了 \(x^a*(n-x)^b\),轉移復雜一些,常數大的過不去

#include<bits/stdc++.h>
using namespace std;
const int N=205;
int n,a,b,mod,c[N][N],m,d;
inline void priwork(){
    for(int i=0;i<N;i++){
        c[i][0]=1;
        for(int j=1;j<=i;j++)c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
    }
}
struct
mat{ int a[N][N]; mat(){} inline mat operator *(const mat &p)const{ mat ret; for(int i=0;i<m;i++) for(int j=0;j<m;j++){ ret.a[i][j]=0; for(int k=0;k<m;k++) if(a[i][k] && p.a[k][j]) ret.a[i][j]=(ret.a[i][j]+1ll*a[i][k]*p.a[k][j])%mod; } return ret; } }S,T; inline void solve(){ for(int i=0;i<d;i++){ T.a[i+d][i]=1;T.a[i][i]=1; for(int j=i;j<d;j++)T.a[i][j+d]=c[j][i]; } int k=n; S.a[0][0]=1; while(k){ if(k&1)S=S*T; T=T*T;k>>=1; } } int main(){ freopen("pp.in","r",stdin); freopen("pp.out","w",stdout); int ans=0; memset(S.a,0,sizeof(S.a));memset(T.a,0,sizeof(T.a)); cin>>n>>a>>b>>mod;d=a+b+1;m=d*2; priwork();solve(); for(int i=0;i<d;i++)S.a[0][i]=(S.a[0][i]+S.a[0][i+d])%mod; for(int i=0,t=1;i<=a;i++,t=1ll*t*n%mod) ans=(ans+1ll*((a-i)&1?-1:1)*c[a][i]*t%mod*S.a[0][a+b-i])%mod; if(ans<0)ans+=mod; cout<<ans<<endl; return 0; }

bzoj 5298: [Cqoi2018]交錯序列