kmjp's blog

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

AtCoder AGC #021 : E - Ball Eat Chameleons

またややこしい設定。
https://agc021.contest.atcoder.jp/tasks/agc021_e

問題

N匹の青いカメレオンがいる。
ここにK個の赤か青のボールを投げこむ。

ボールを投げるといずれかのカメレオンがそれを食べる。
カメレオンが最後に食べたボールにより、赤青のボール数の差が生じると、カメレオンがその色になる。

K個投げ込んだ結果、カメレオンがすべて赤くなるようなボールの色順は何通りか。

解法

ボール数が等しいときは直前の状態を引き継ぐので少しややこしい。
カメレオンが赤いためには、食べた赤ボールが青ボールより多いか、同数だが最後に食べたボールが青(言い換えれば最後の直前は赤を多く食べていた状態である)ことが求められる。

これを踏まえ、赤ボール数Rを総当たりすることを考えよう。
青ボール数をBとすると、R≧Bでなければならないのは明らかである。

R>Bの場合

最小で(N-(R-B))体は先に赤を1個たべ、あとに1個青を食べることになる。
すると、残り(R-B)体が食べるのはR-(N-(R-B))個の赤とB-(N-(R-B))=R-N個の青であり、これは前者の方が大きいので途中経過は問わず最後は赤くなる。
そこで、(N-(R-B))体が「赤を1個たべ、あとに1個青を食べる」ケースを考える。
投げるボール順を見て、赤が多い間は問題ないが、青が多くなると(N-(R-B))体の方は赤くなれないものが生じかねない。
ただしB-(N-(R-B))=R-N個までなら残り1体が食べる分として吸収できる。

これを踏まえると、この組み合わせは以下のようになる。
2次元座標で(0,0)から(R,B)までxまたはy座標が増加する向きに格子点を辿る組み合わせを考える。
ただしy-x>R-N、すなわりy-x≧R-N+1となるケースは認められない。
これはカタラン数の求め方を応用すると容易に求められ、 \displaystyle {}_{R+B} C_B - {}_{R+B} C_{B-(R-N+1)}となる。

R==Bの場合

全てのカメレオンが赤青同数のボールを食べる。
よって少なくとも最後の1体が食べる最後のボールは赤くなければならない。
これは(0,0)から(R,B)ではなく(0,0)から(R,B-1)に至る経路を数え上げればよい。

int N,K;
ll mo=998244353;

ll combi(ll N_, ll C_) {
	const int NUM_=1400001;
	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 catalan_arrange(int X,int Y,int T) {
	return (combi(X+Y,Y)-combi(X+Y,Y-T)+mo)%mo;
}


void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>N>>K;
	
	ll ret=0;
	for(int R=N;R<=K;R++) {
		int B=K-R;
		if(R==B) {
			ret+=catalan_arrange(R,B-1,(R-N+1));
		}
		if(R>B) {
			ret+=catalan_arrange(R,B,(R-N+1));
		}
	}
	
	cout<<ret%mo<<endl;
}

まとめ

前のCFでも出たけど、カタラン数の数え上げ問題苦手。