Cに続きDも知らない解法が出てきて勉強になります。
https://atcoder.jp/contests/dwacon5th-final-open/tasks/dwacon5th_final_d
問題
'('')'が同数からなるN文字の文字列Sが与えられる。
1~NのPermutation Pのうち、SをPに応じて位置を置換したとき、正しい括弧列となるPにおける転倒数の総和を求めよ。
解法
M=N/2とする。
元のSにおける閉じ括弧と開き括弧の転倒数をIとする。
理由はEditorialを見てもらうとして、解はIと線形の関係にありA(M)+B(M)*Iとなる。
A(M)とB(M)はMで定まる値である。
そこで、両極端な例からA(M)、B(M)を求めよう。
I=0、すなわちS='((('...')))'の時の解がA(M)、その正反対の時のS=')))'...'((('の解がA(M)+B(M)*M^2となる。
ここからA(M),B(M)を求めよう。
ここでは前者のケースのみ説明する。後者の場合漸化式がちょっと変わるだけで式にはない。
S='((('...')))'をPで置換した後、正しい括弧列である場合の(括弧ではなくPの)転倒数を考えよう。
'('同士、および')'同士の転倒数は、確率1/2で転倒すると考えると容易に求められる。
問題は')'と'('の転倒数である。
- C(m) := m番目のカタラン数
- D(m) := m個の開き括弧と閉じ括弧が正しい括弧列を成すケースにおける、開き括弧と閉じ括弧の転倒数の総和
よって、D(m)を求めたい。
D(m)を考える際、最初の開き括弧に対応する閉じ括弧までに2i文字あるケースを考えると
となる。Editorialでは、sumの中の最初の2つの積和を一旦ばらしてまとめ、
としている(C(a)D(b)!=C(b)D(a)なので、元のsumの中は直接くっつけることはできない。)
これでO(N^2)で求められることになるが、当然これはTLEする。
解のmod 10^9+7を求めたいので、フーリエ変換に持ち込むのも厳しい。
そこでD(m)、C(m)の母関数を考え式変形する。
となすると、先ほどの式からとなる。
式変形してになる。ここで、Wikipediaのカタラン数の項目に母関数P(t)の一般項を代入しよう。
するととなる。P(t)はCombinationを使い容易に計算できるので、多項式を(1-4t)で割っていくことを考えるとQ(t)の各項が全部でO(N)で求められる。
以下、コメントアウト部分は元のO(N^2)解法である。
int N,M; string S; ll mo=1000000007; const int NUM_=1400001; ll fact[NUM_+1],factr[NUM_+1],inv[NUM_+1]; ll D[505050],D2[505050]; ll comb(ll N_, ll C_) { if(C_<0 || C_>N_) return 0; return factr[C_]*fact[N_]%mo*factr[N_-C_]%mo; } ll catalan(int n) { return fact[2*n]*factr[n]%mo*factr[n+1]%mo; } void solve() { int i,j,k,l,r,x,y; string s; inv[1]=fact[0]=factr[0]=1; for (int i=2;i<=NUM_;++i) inv[i] = inv[mo % i] * (mo - mo / i) % mo; for (int i=1;i<=NUM_;++i) fact[i]=fact[i-1]*i%mo, factr[i]=factr[i-1]*inv[i]%mo; cin>>N>>S; M=N/2; /* D[1]=0; D2[1]=1; for(i=2;i<=M;i++) { for(j=1;j<=i;j++) { (D[i]+=catalan(i-j)*D[j-1]+catalan(j-1)*D[i-j]+1LL*j*(i-j)%mo*catalan(j-1)%mo*catalan(i-j))%=mo; (D2[i]+=catalan(i-j)*D2[j-1]+catalan(j-1)*D2[i-j]+(2*(j-1)+1+1LL*j*(i-j))%mo*catalan(j-1)%mo*catalan(i-j))%=mo; } } */ for(i=2;i<=M;i++) D[i]=(i-1)*catalan(i-1)%mo; FOR(i,M+1) (D[i+1]+=4*D[i])%=mo; FOR(i,M+1) D2[i]=((catalan(i)*i%mo*i-D[i])%mo+mo)%mo; ll Am=(1LL*M*(M-1)/2%mo*catalan(M)+D[M])%mo*fact[M]%mo*fact[M]%mo; ll AmpBm=(1LL*M*(M-1)/2%mo*catalan(M)+D2[M])%mo*fact[M]%mo*fact[M]%mo; ll Bm=(AmpBm-Am+mo)%mo*((inv[M]*inv[M])%mo)%mo; ll rev=0,sum=0; FORR(c,S) { if(c==')') rev++; else sum+=rev; } cout<<(Am+Bm*(sum%mo))%mo<<endl; }
まとめ
これはどこもかしこも思いつく気がしない…。