Codeforces #225(383 Div1) B. Volcanoes

問題

nxnのグリッドがあり(1, 1)から(n, n)へ、上または右に移動することを繰り返して進む。
グリッドのうちm個には障害物があり、そのマスへは入れない。


(1, 1)から(n, n)へ進むことはできるか。

制約条件

n≦10^9
m≦10^5

方針

しんどい実装問。

上または右にしかいけないので、下から一行ずつ、行ける範囲を更新していけばよい。
ナイーブにやるとO(m^2)とかだけど、更新があるのは石があるところだけなので、
行ごとに石のある場所を持ってあげる、現在通行不能な範囲を平衡二分木で管理するとかやると全体でO(n + mlogm)になる。

ソースコード

いくらなんでももう少しマシな書き方があったはず。

const int MX = 100010;
int n, m, x[MX], y[MX];
bool dp[3 * MX];
vi v[3 * MX];

inline void fix(set<pi> &iv, const pi &p){
	int a = p.first, b = p.second;
	set<pi>::iterator it = iv.lower_bound(mp(a, b));
	if(it != iv.end() && ++it != iv.end() && it->first == b){
		int c = it->second;
		iv.erase(mp(a, b));
		iv.erase(mp(b, c));
		iv.insert(mp(a, c));
		fix(iv, mp(a, c));
		return;
	}
	it = iv.lower_bound(mp(a, b));
	if(it != iv.begin() && (--it)->second == a){
		int c = it->first;
		iv.erase(mp(a, b));
		iv.erase(mp(c, a));
		iv.insert(mp(c, b));
		fix(iv, mp(c, b));
	}
}
inline void ins(set<pi> &iv, int x){
	set<pi>::iterator it = iv.lower_bound(mp(x + 1, 0));
	if(it != iv.begin()){
		--it;
		if(it->first <= x && x < it->second) return;
	}
	iv.insert(mp(x, x + 1));
	fix(iv, mp(x, x + 1));
}
inline void del(set<pi> &iv, int x){
	set<pi>::iterator it = iv.lower_bound(mp(x + 1, 0));
	if(it == iv.begin()) return;
	--it;
	
	int a = it->first, b = it->second;
	if(x < a || x >= b) return;
	iv.erase(mp(a, b));
	if(a < x) iv.insert(mp(a, x));
	if(x + 1 < b) iv.insert(mp(x + 1, b));
}

int main(){
	scanf("%d%d", &n, &m);
	vi xs, ys;
	rep(i, m){
		scanf("%d%d", x + i, y + i);
		rep(j, 3){
			if(x[i] - 1 + j > 0 && x[i] - 1 + j <= n) xs.pb(x[i] - 1 + j);
			if(y[i] - 1 + j > 0 && y[i] - 1 + j <= n) ys.pb(y[i] - 1 + j);
		}
	}
	
	xs.pb(1); ys.pb(1); xs.pb(n); ys.pb(n);
	sort(all(xs)); xs.erase(unique(all(xs)), xs.end());
	sort(all(ys)); ys.erase(unique(all(ys)), ys.end());
	
	rep(i, m){
		int b = lower_bound(all(ys), y[i]) - ys.begin();
		v[b].pb(lower_bound(all(xs), x[i]) - xs.begin());
	}
	rep(i, ys.size()) sort(all(v[i]));
	
	int lim = v[0].size() ? v[0][0] : xs.size();
	rep(i, lim) dp[i] = 1;
	
	set<pi> iv;
	if(lim < xs.size()) iv.insert(mp(lim, xs.size()));
	
	rep(i, (int)ys.size() - 1){
		
		vi er;
		each(j, iv){
			
			int l = j->first;
			if(l == 0 || !dp[l - 1]) continue;
			
			l = upper_bound(all(v[i + 1]), l) - v[i + 1].begin() - 1;
			if(l >= 0 && (v[i + 1][l] == j->first || v[i + 1][l] == j->first - 1)) continue;
			
			int r = l + 1 < v[i + 1].size() ? v[i + 1][l + 1] : xs.size();
			r = min(r, j->second);
			
			for(int k = j->first; k < r; k++){
				dp[k] = 1;
				er.pb(k);
			}
		}
		rep(j, er.size()) del(iv, er[j]);
		rep(j, v[i + 1].size()) ins(iv, v[i + 1][j]), dp[v[i + 1][j]] = 0;
	}
	cout << (dp[xs.size() - 1] ? 2 * n - 2 : -1) << endl;
	
	return 0;
}