kmjp's blog

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

AtCoder ARC #149 : D - Simultaneous Sugoroku

どうにか解けて良かったね。
https://atcoder.jp/contests/arc149/tasks/arc149_d

問題

1次元の数直線上にN個の駒があり、それぞれの初期位置が与えられる。
それぞれ以下のクエリが計M個与えられる。

  • 整数値Dが与えられる。駒を原点の向きにDだけ移動する。ただし駒がすでに原点にあるならそれ以上移動しない。

各駒が原点に停止するのは何回目の移動か。
もし原点に停止しない場合、最後の位置はどこか。

解法

登場し得る座標は-10^6~10^6である。
そこで、初期状態で[1,1000000]の区間を、平行移動しよう。
また、負の座標に入ってしまった区間は原点を中心に折り返そう。
その際、折り返して重なった部分はUnion-Findの要領でマージしていく。
そして初期状態の各位置が、いつ原点に行くのか、また最後どの位置に行くのかを計算していこう。

int N,M;
int X[303030],D[303030];

int ret[1010101];
int pos[1010101];

template<int um> class UF {
	public:
	vector<int> par,rank,cnt;
	UF() {par=rank=vector<int>(um,0); cnt=vector<int>(um,1); for(int i=0;i<um;i++) par[i]=i;}
	void reinit(int num=um) {int i; FOR(i,num) rank[i]=0,cnt[i]=1,par[i]=i;}
	int operator[](int x) {return (par[x]==x)?(x):(par[x] = operator[](par[x]));}
	int count(int x) { return cnt[operator[](x)];}
	int operator()(int x,int y) {
		if((x=operator[](x))==(y=operator[](y))) return x;
		cnt[y]=cnt[x]=cnt[x]+cnt[y];
		if(rank[x]>rank[y]) return par[x]=y;
		rank[x]+=rank[x]==rank[y]; return par[y]=x;
	}
};
UF<1<<20> uf;

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	
	cin>>N>>M;
	FOR(i,N) cin>>X[i];
	FOR(i,M) cin>>D[i];
	vector<pair<pair<int,int>,int>> S={{{1,1000000},1}};
	FOR(i,M) {
		vector<pair<pair<int,int>,int>> T;
		FORR2(p,f,S) {
			if(p.first>0) {
				p.first-=D[i];
				p.second-=D[i];
			}
			else {
				p.first+=D[i];
				p.second+=D[i];
			}
			if(p.first>0||p.second<0) {
				T.push_back({p,f});
			}
			else {
				if(p.first<0) T.push_back({{p.first,-1},f});
				if(p.second>0) T.push_back({{1,p.second},f+(1-p.first)});
				x=f-p.first;
				ret[uf[x]]=i+1;
			}
		}
		sort(ALL(T));
		S.clear();
		int pre=0;
		
		FORR(v,T) {
			int L=v.first.first,R=v.first.second,F=v.second;
			x=lower_bound(ALL(S),v)-S.begin();
			if(x&&S[x].first.first>L) x--;
			while(L<=R) {
				if(x==S.size()) break;
				if(L>S[x].first.second) {
					x++;
					continue;
				}
				uf(S[x].second+L-S[x].first.first,F);
				L++;
				F++;
			}
			if(L<=R) {
				S.push_back({{L,R},F});
			}
		}
	}
	FORR2(a,b,S) {
		for(i=a.first;i<=a.second;i++) pos[uf[b+i-a.first]]=i;
	}
	
	FOR(i,N) {
		x=uf[X[i]];
		if(ret[x]) {
			cout<<"Yes "<<ret[x]<<endl;
		}
		else {
			cout<<"No "<<pos[x]<<endl;
		}
			
	}
	
}

まとめ

今思えばもう少しすっきり書けるかもな。