kmjp's blog

競技プログラミング参加記です

yukicoder : No.1873 Bracket Swapping

聞いてしまうとすんなりに見えるな。
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;
	
}

まとめ

初手でこれすんなり思いつくかな…。