聞いてしまうとすんなりに見えるな。
https://yukicoder.me/problems/no/1873
問題
2N文字の正規括弧列Sが与えられる。
ここから2か所の文字を選びswapする作業をK回繰り返す。
その結果得られる文字列も、正規括弧列であるケースは何通りか。
解法
まず、SにK回処理を加えた場合、2n箇所元と異なっている(閉じ括弧が開き括弧になる箇所と、その反対がn箇所ずつ)ような文字列は何通り作れるかを考える。
nが等しい文字列であれば、異なっている場所によらず、構築手順の組み合わせは等しいはずである。
その数をf(n)とすると、2n箇所異なっているようなケース全体を行列累乗で求めれば、それをComb(N,n)^2で割ればf(n)が得られる。
これができるとあとは簡単。
g(i,k,n) := 2N文字の括弧列の先頭i文字を考えたとき、開き括弧がk回多く登場していて、n箇所Sと異なっているような文字列の組み合わせ
これはO(N^3)で求められる。
あとはg(2N,0,2n)*f(n)の総和を取ろう。
const ll mo=998244353; const int MAT=101; struct Mat { ll v[MAT][MAT]; Mat(){ZERO(v);};}; ll modpow(ll a, ll n = mo-2) { ll r=1; while(n) r=r*((n%2)?a:1)%mo,a=a*a%mo,n>>=1; return r; } Mat mulmat(Mat& a,Mat& b,int n=MAT) { ll mo2=4*mo*mo; int x,y,z; Mat r; FOR(x,n) FOR(y,n) r.v[x][y]=0; FOR(x,n) FOR(z,n) FOR(y,n) { r.v[x][y] += a.v[x][z]*b.v[z][y]; if(r.v[x][y]>mo2) r.v[x][y] -= mo2; } FOR(x,n) FOR(y,n) r.v[x][y]%=mo; return r; } Mat powmat(ll p,Mat a,int n=MAT) { int i,x,y; Mat r; FOR(x,n) FOR(y,n) r.v[x][y]=0; FOR(i,n) r.v[i][i]=1; while(p) { if(p%2) r=mulmat(r,a,n); a=mulmat(a,a,n); p>>=1; } return r; } ll comb(ll N_, ll C_) { const int NUM_=400001; static ll fact[NUM_+1],factr[NUM_+1],inv[NUM_+1]; if (fact[0]==0) { 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; } if(C_<0 || C_>N_) return 0; return factr[C_]*fact[N_]%mo*factr[N_-C_]%mo; } ll C2(ll a) { a=a*(a-1)/2; return a%mo; } int N; string S; int K; ll W[202]; ll dp[202][102][102]; void solve() { int i,j,k,l,r,x,y; string s; cin>>S>>K; N=S.size()/2; Mat A; for(i=0;i<=N;i++) { int same=N-i; int dif=i; //同種2つ A.v[i][i]=C2(N)+C2(N)+2*same*dif; if(i) A.v[i-1][i]=dif*dif; if(i<N) A.v[i+1][i]=same*same; } A=powmat(K,A); dp[0][0][0]=1; FOR(i,2*N) { FOR(j,N+1) FOR(x,N+1) { (dp[i+1][j+1][x]+=dp[i][j][x])%=mo; if(j) (dp[i+1][j-1][x+(S[i]=='(')]+=dp[i][j][x])%=mo; } } ll ret=0; FOR(i,N+1) { W[i]=A.v[i][0]*modpow(comb(N,i))%mo*modpow(comb(N,i))%mo; (ret+=W[i]*dp[2*N][0][i])%=mo; } cout<<ret<<endl; }
まとめ
初手でこれすんなり思いつくかな…。