kmjp's blog

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

Codeforces #930 : Div1 B. Pinball

ちょっと出来の悪かった回。
https://codeforces.com/contest/1936/problem/B

問題

左右にNマスが並んでおり、各マス右矢印か左矢印が並んでいる。
どこかのマスにボールを置くと、以下のように状態が変わる。

  • ボールがマスの矢印の向きに1つ移動する。
  • その後、元のマスの矢印の向きが反転する。

各マスにボールを置いた時それぞれについて、ボールがNマス外に出るまでに何マス移動するか答えよ。

解法

ボールの移動方向が反転する回数は、初期位置の左側にある右向きの矢印と、右側にある左向きの矢印で決まる。
また、移動距離はそれらの座標で決まる。

両矢印のマスの座標の一覧と、座標の累積和をもっておけば、上記はO(logN)で計算できる。

int T,N;
string S;
vector<int> L,R;
vector<ll> LS,RS;

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>T;
	while(T--) {
		cin>>N>>S;
		L.clear();
		R.clear();
		LS={0LL};
		RS={0LL};
		FOR(i,N) {
			if(S[i]=='<') {
				L.push_back(i);
				LS.push_back(LS.back()+i);
			}
			else {
				R.push_back(i);
				RS.push_back(RS.back()+i);
			}
		}
		
		FOR(i,N) {
			int nr=lower_bound(ALL(R),i+1)-R.begin();
			int nl=L.end()-lower_bound(ALL(L),i);
			ll ret=-1;
			if(S[i]=='<') {
				nl=min(nl,nr+1);
				nr=min(nr,nl);
				if(nl==nr) {
					x=lower_bound(ALL(L),i)-L.begin();
					ret=N+i+2*(LS[x+nl]-LS[x+1]);
					x=lower_bound(ALL(R),i)-R.begin();
					ret-=2*(RS[x]-RS[x-nr]);
				}
				else {
					x=lower_bound(ALL(L),i)-L.begin();
					ret=i+2*(LS[x+nl]-LS[x+1])+1;
					x=lower_bound(ALL(R),i)-R.begin();
					ret-=2*(RS[x]-RS[x-nr]);
				}
				
			}
			else {
				nr=min(nr,nl+1);
				nl=min(nl,nr);
				if(nl==nr) {
					x=lower_bound(ALL(L),i)-L.begin();
					ret=2*(LS[x+nl]-LS[x]);
					x=lower_bound(ALL(R),i+1)-R.begin();
					ret-=2*(RS[x]-RS[x-nr])-R[x-1];
					ret++;
				}
				else {
					x=lower_bound(ALL(L),i)-L.begin();
					ret=N+2*(LS[x+nl]-LS[x]);
					x=lower_bound(ALL(R),i+1)-R.begin();
					ret-=2*(RS[x]-RS[x-nr])-R[x-1];
				}
			}
			cout<<ret<<" ";
		}
		cout<<endl;
		
	}
}

まとめ

これそんなに難しく見えないけど本番なんか苦戦してるな…。