kmjp's blog

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

TopCoderOpen 2021 Regional Qualification Round2 : Div1 Hard ParkingSequences

TLE対応に手間取った。
https://community.topcoder.com/stat?c=problem_statement&pm=17124&rd=18774

問題

車N台分のスペースが円形に並んでいる駐車場がある。
そこにN人の人が車を止めることを考える。

各人、最初に止めようとする位置が決まっているとする。
そこが空いていればそこに止めるし、埋まっていたら時計回りに回って隣のスペースに止めようとする。
最終的に空きスペースがあるまで時計回りに回る。

N人が最初に止めようとする位置の組み合わせはN^N通り考えられる。
その際、「埋まっていたら」の条件がちょうどB回発生するのは何通りか。

解法

まずこの問題を考える上で、止める順番は気にしなくてよい。
i番目の人とj番目の人が同じ位置に止めようとしたとして、どちらが先でどちらが後でも、「埋まっていたら」の条件の発生回数の合計は一緒である。
そこで、以降は順番を無視して、その位置に止めようとする人が何人いたかだけを考えてよい。
あとは以下を考える。
f(i,c,n,s,z) := (N-1)番目の位置に止めたくて止められない人がi人いたとき、0番目以降n番目まで止めようとする人数を定めたとき、n番目から溢れた人がc人で、ここまで「埋まっていたら」の条件が発生した回数がs回で、かつc=0だったことがあるかどうかを示す真偽値がzであるときの、組み合わせの数
とする。

「(N-1)番目の位置に止めたくて止められない人がi人いたとき」を前提として処理を回すので、実際(N-1)番目の位置まで処理をしたとき、i人があふれていないといけない。
なので解はf(i,i,N-1,B,True)の総和となる。
zの有無について、仮に全員がバラバラの位置に止めようとした場合、本来「埋まっていたら」は1回も発生しないはずなのだが、i>0のケースを考えると「埋まっていたら」がi*N回発生してしまう。
これは不自然である。
実際は、最初に止めようとする位置がどのような状態であっても、どこかでc=0となる位置が存在するので、そこだけ重複なく数えるようにする。

なお、この問題はBが微妙に小さく設定されている。
状態の数が多く、ループ回数が非常にかさむため、この問題は愚直に処理するとTLEする。
Bが小さいことを利用し、早めにループを抜けるようにして極力ループ回数を抑えよう。

ll from[2][21][51][202];
ll to[2][21][51][202];

const ll mo=1000000007;
static const int N_=102;
static ll C_[N_][N_];
int fix[51][51];

class ParkingSequences {
	public:
	int count(int N, int B) {
		int i,j;
		FOR(i,N_) C_[i][0]=C_[i][i]=1;
		for(i=1;i<N_;i++) for(j=1;j<i;j++) C_[i][j]=(C_[i-1][j-1]+C_[i-1][j])%mo;
		
		FOR(i,50) {
			for(j=1;j<=50;j++) {
				fix[i][j]=fix[i][j-1]+max(0,(i-j));
			}
		}
		
		ll ret=0;
		for(i=0;i<=min(N-1,20);i++) {
			ZERO(from);
			from[0][i][N][0]=1;
			FOR(j,N) {
				ZERO(to);
				for(int NZ=0;NZ<=1;NZ++) {
					for(int C=0;C<=20;C++) {
						for(int S=0;S+fix[C][N-j]<=B;S++) {
							for(int L=0;L<=N;L++) if(from[NZ][C][L][S]) {
								for(int add=(C==0?1:0);add<=L&&S+fix[C+add][N-j]<=B;add++) {
									(to[NZ|(C+add-1==0)][C+add-1][L-add][S+(C+add-1)]+=from[NZ][C][L][S]*C_[L][add])%=mo;
								}
							}
						}
					}
				}
				swap(from,to);
			}
			ret+=from[1][i][0][B];
		}
		return ret%mo;
		
		
	}
}

まとめ

本番出てたらTLEしてたわ。