この行列がすんなり出てこなかった。
http://yukicoder.me/problems/no/444
問題
N種類の旨み成分があり、それぞれの旨みはA[i]である。
これらから重複を許して計C個の旨みを抜き出す。
2種類以上の旨みを抜き出したとき、C個の旨みの積を全体の旨みとなる。
C個の旨みの抜き出し方における全体の旨みの総和を求めよ。
解法
1種類の旨みだけでC個選ぶ場合の全体の旨みは容易に求められるので、「2種類以上~~」の条件は無視してC個の旨みの積の総和だけを考えて、最後に1種類だけ選んだケースを引けば良い。
以下の状態を考える。(書き方は違うが値はEditorialと同じ)
f(c,n) := 先頭n種類の成分から全体でc個の旨みを選ぶ場合の旨みの積の総和(ただしc==0の時は1とする。)
求めたいものはf(C,N)である。
c個目にi番を選ぶには、(c-1)個目を選ぶ際i番以下を選んでいなければいけないので、以下の式が成り立つ。
以下のようにf(c,n)と、漸化式の係数をベクトル・行列の形に並べると、f(c,n)を行列累乗の形で計算できるようになる。
なお、1種類の旨みだけで構成されるケースはA[i]のC乗を別途計算する必要はなく、B^Cの対角成分にA[i]^Cが並ぶのでこれを引けば良い。
int N; ll C; ll mo=1000000007; const int MAT=80; ll G[MAT][MAT],G2[MAT][MAT]; void powmat(ll p,int n,ll a[MAT][MAT],ll r[MAT][MAT]) { int i,x,y,z; ll a2[MAT][MAT]; FOR(x,n) FOR(y,n) a2[x][y]=a[x][y]; FOR(x,n) FOR(y,n) r[x][y]=(x==y); while(p) { ll h[MAT][MAT]; if(p%2) { FOR(x,n) FOR(y,n) h[x][y]=0; FOR(x,n) FOR(z,n) FOR(y,n) h[x][y] += (r[x][z]*a2[z][y]) % mo; FOR(x,n) FOR(y,n) r[x][y]=h[x][y]%mo; } FOR(x,n) FOR(y,n) h[x][y]=0; FOR(x,n) FOR(z,n) FOR(y,n) h[x][y] += (a2[x][z]*a2[z][y]) % mo; FOR(x,n) FOR(y,n) a2[x][y]=h[x][y]%mo; p>>=1; } } void solve() { int i,j,k,l,r,x,y; string s; cin>>N>>C; FOR(i,N) { cin>>r; FOR(x,i+1) G[i][x]=r; } powmat(C,N,G,G2); ll ret=0; FOR(x,N) ret+=mo+G2[x][0]-G2[x][x]; cout<<ret%mo<<endl; }
まとめ
勉強になりました。