Hatena::Grouptopcoder

kojingharangの日記 RSSフィード

|

2014-09-17

SRM 633 Div1 250 PeriodicJumping (追記あり

02:25 |  SRM 633 Div1 250 PeriodicJumping (追記あり - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 633 Div1 250 PeriodicJumping (追記あり - kojingharangの日記  SRM 633 Div1 250 PeriodicJumping (追記あり - kojingharangの日記 のブックマークコメント

  • 2次元平面の原点にかえるがいる。i回目は距離L[i%N]だけ任意の方向に移動できる。(x, 0) に移動するための最小回数を求めよ。無理なら -1 を返す.
  • 1≦|L|=N≦50, 1≦|L[i]|≦10^9, |x|≦10^9
  • 任意の方向に移動できるってやばそう. √2 とかどうすんだよ
  • 考察考察
  • i回目までの距離の和 < |x| なら無理
  • |x|≦A なる A が 2 回現れればそこまでで必ず (x, 0) に行ける. ( (0,0) → (x/2, sqrt(A^2-(x/2)^2)) → (x, 0) と行けばいい)
  • これらは解の範囲を制限するものであるがまったくもって十分ではない。はて.....
  • i回目が終わった時に可能性としてありうる点の原点からの距離 in [lo, hi] を管理したら筋が良さそう
  • 0回目は [0, 0], 1回目は [ L[0], L[0] ]
  • 前回の [lo, hi] と今回の距離 L[i] があったら基本的には lo±L[i], hi±L[i] とかになりそう。ただし距離なので絶対値をとる
  • lo-L[i] と hi-L[i]の符号が異なる or lo+L[i] と hi+L[i] の符号が異なるときは lo ← 0 になる
  • (追記) lo+L[i] と hi+L[i] はどちらも正なのでこの条件はなくていいですね。根本的には ?-L[i]==0 となる ? in [lo, hi] があればよいので同値な (lo-L[i])(hi-L[i]) ≦ 0 をチェックすれば良いということですね。
  • ここまでで小さい x については OK
  • lo はそのうち 0 になるかも
  • ひとたび lo==0 になったらそれ以降は lo==0 である. そしたら hi だけ伸びてくので規則性がありそう.
  • (追記) 一般的には lo==0 のときにすげー大きい数が来たら lo > 0 になりますね。周期性があるからいつか lo==0になるわけですね。
  • lo==0 になったところで L を一巡移動したときの hi の増分を覚えておいて |x|-hi を割ってその回数*Nだけ答えに加えればよさげ
  • 現実的な回数で lo==0 になるのか? →同じ数が2回ずつ現れれば必ず lo==0 になるので、最大2巡で lo==0 になる。これで良さそう。
  • 提出
  • いつか必ず lo==0 になるということは、hi は増える一方なので -1 が答えになることはない。
  • x=1, {2,5} でチャレンジ1コ成功. (正解は 3 回)
  • Accepted
class PeriodicJumping {
	public:
	int minimalTime(int x, vector <int> L) {
		if(x==0) return 0;
		int N=L.size();
		double lo=0, hi=0;
		ll ans = 0;
		REP(loop, 100000) {
			double start=-1;
			if(lo==0) start=hi;
			REP(i, N) {
				double lolo = lo-L[i];
				double lohi = lo+L[i];
				double hilo = hi-L[i];
				double hihi = hi+L[i];
				lo = min(abs(lolo), abs(hilo));
				hi = max(abs(hilo), abs(hihi));
				if(lolo * hilo < 0 || lohi * hihi < 0) lo=0;
				ans++;
//				cout<<lo<<" "<<hi<<endl;
				if(lo-1e-9<=abs(x) && abs(x)<=hi+1e-9) return ans;
			}
			if(start >= 0) {
				double d = hi - start;
				ll loop = floor((abs(x) - hi) / d);
				if(loop > 10000) {
					loop-=4;  // 特急に乗る. ただしちょい手前で減速! (安全第一
//					cout<<" d "<<d<<" "<<loop<<" "<<ans<<endl;
					ans += loop*N;
					hi += d * loop;
				}
			}
		}
		return -1;
	}
};

2014-08-30

SRM 631 Div1 500 CatsOnTheLineDiv1

04:16 |  SRM 631 Div1 500 CatsOnTheLineDiv1 - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 631 Div1 500 CatsOnTheLineDiv1 - kojingharangの日記  SRM 631 Div1 500 CatsOnTheLineDiv1 - kojingharangの日記 のブックマークコメント

  • 猫が数直線上の整数位置 p[i] のとこに c[i] 匹いる。各自1ターンで-1,0,+1 動ける。Tターン後に猫が2匹以上いる位置の数の最小値を求めよ。
  • 1≦猫の数≦1000, |p[i]|≦10^8, c[i]≦10^8, 1≦T≦10^8
  • かたまりが1コの場合、c[i]/2 ターンあればそいつらは厚さ1に展開できる。
  • 展開できないならどっちみち2匹以上の場所が1個以上できるので、余計に散らばらない分だけ塊のままでいる方が良い。
  • 猫は [p[i]-T, p[i]+T] まで行けるので, 展開後の一番左の猫がいる範囲は [ p[i]-T, p[i]+T-c[i]+1 ]
  • 一番左のかたまりは、展開できるならば一番左に展開するのが良い。
  • 次のかたまりも、前のと重ならない範囲で展開できるならば一番左に展開する
  • ... 結局左寄せ貪欲でいいんでは
  • だめならかたまりのまま可能な限り右に置く
  • みたいな

  • Failed system test

(あとで)

  • 展開できて前のと重ならないかどうかチェックするまえに、直近塊で置いたやつのとこにマージできるかを最初に見るべきだったようだ。
  • 展開して最後に置いた位置が右に移動しちゃう より 塊にマージして最後に置いた位置変わらず の方がいい。
  • 安全にDP(など微妙な貪欲より計算量は悪いけど十分解ける時間だしより安全な方法)で...という解き方が課題である。。
  • passed in practice room
class CatsOnTheLineDiv1 {
	public:
	int getNumber(vector <int> P, vector <int> C, int T) {
		int N=P.size();
		vector<pair<int, int> > w(N);
		REP(i, N) w[i] = MP(P[i], C[i]);
		sort(ALL(w));
		REP(i, N) P[i]=w[i].first, C[i]=w[i].second;
		int ans = 0;
		ll next = -(1LL<<60);
		ll m = -(1LL<<60);
		REP(i, N) {
			ll hl = P[i]-T;
			ll hr = P[i]-C[i]+1+T;
			if(P[i]-T<=m && m<=P[i]+T) continue; // これを最初にチェックすべき
			if(hr<next || C[i]/2 > T) {
				ans++;
				m = P[i]+T;
			} else {
				next = max(next, hl) + C[i];
			}
		}
		return ans;
	}
};

2014-08-28

Codeforces #253 Div2 D. Appleman and Tree (より原理的にしてみる)

18:02 |  Codeforces #253 Div2 D. Appleman and Tree (より原理的にしてみる) - kojingharangの日記 を含むブックマーク はてなブックマーク -  Codeforces #253 Div2 D. Appleman and Tree (より原理的にしてみる) - kojingharangの日記  Codeforces #253 Div2 D. Appleman and Tree (より原理的にしてみる) - kojingharangの日記 のブックマークコメント

g(node, i, なし) は
  g(node, i-1, なし) × --- g(i番目の子, all, なし) ... OK
+ g(node, i-1, なし) × -|- g(i番目の子, all, なし) ... だめ(i番目の子以下が黒なしで確定しちゃう
+ g(node, i-1, なし) × --- g(i番目の子, all, あり) ... だめ
+ g(node, i-1, なし) × -|- g(i番目の子, all, あり) ... OK
+ g(node, i-1, あり) × --- g(i番目の子, all, なし) ... だめ
+ g(node, i-1, あり) × -|- g(i番目の子, all, なし) ... だめ(i番目の子以下が黒なしで確定しちゃう
+ g(node, i-1, あり) × --- g(i番目の子, all, あり) ... だめ
+ g(node, i-1, あり) × -|- g(i番目の子, all, あり) ... だめ

g(node, i, あり) は
  g(node, i-1, なし) × --- g(i番目の子, all, なし) ... だめ
+ g(node, i-1, なし) × -|- g(i番目の子, all, なし) ... だめ
+ g(node, i-1, なし) × --- g(i番目の子, all, あり) ... OK
+ g(node, i-1, なし) × -|- g(i番目の子, all, あり) ... だめ
+ g(node, i-1, あり) × --- g(i番目の子, all, なし) ... OK
+ g(node, i-1, あり) × -|- g(i番目の子, all, なし) ... だめ
+ g(node, i-1, あり) × --- g(i番目の子, all, あり) ... だめ
+ g(node, i-1, あり) × -|- g(i番目の子, all, あり) ... OK
  • Accepted in Practice room
  • 問題の条件をよりそのままコードに落とした感は出てきたが、全体的に簡潔さはあんまし変わってない気がする。。
VVI g;
VVI memo;
vector<bool> black;

// g(idx, all, b)
ll dfs(int prev, int idx, int b) {
	if(memo[b][idx]>=0) return memo[b][idx];

	// このノードと子ノード[0-i]以下からなる subtree を条件を満たすように区切ったとき, node を含む領域に含まれる黒の数 -> 何通り
	modll dp[2];
	dp[0] = (modll)!black[idx];
	dp[1] = (modll)black[idx];
	REP(i, g[idx].size()) {
		int ni = g[idx][i];
		if(prev==ni) continue;
		modll ndp[2] = {0, 0};
		modll ch[2] = {dfs(idx, ni, 0), dfs(idx, ni, 1)};
		REP(pi, 2) REP(ci, 2) REP(cut, 2) {
			int obi = pi + (cut ? 0 : ci);
			if(cut && ci==0) continue; // 切断して子以下に黒がなかったらだめ
			if(obi < 2) ndp[obi] += dp[pi] * ch[ci];
		}
		dp[0] = ndp[0];
		dp[1] = ndp[1];
	}
	return memo[b][idx] = dp[b];
}

int main() {
	ll N,p,v;
	while(cin>>N) {
		g = VVI(N);
		memo = VVI(2, VI(N, -1));
		black = vector<bool>(N);
		REP(i, N-1) {
			cin>>p;
			g[i+1].PB(p);
			g[p].PB(i+1);
		}
		REP(i, N) {
			cin>>v;
			black[i] = v;
		}
		ll ans = dfs(-1, 0, 1);
		cout<<ans<<endl;
	}
	
	return 0;
}

2014-08-27

Codeforces #263 Div2 D. Appleman and Tree

16:22 |  Codeforces #263 Div2 D. Appleman and Tree - kojingharangの日記 を含むブックマーク はてなブックマーク -  Codeforces #263 Div2 D. Appleman and Tree - kojingharangの日記  Codeforces #263 Div2 D. Appleman and Tree - kojingharangの日記 のブックマークコメント

  • ノードが白か黒に塗られた木が与えられる。辺を任意個消した時にできる部分木がすべて1個だけ黒を含むようにする方法は何通りあるか? (mod 10^9+7 で)
  • 2≦|ノード数|≦10^5
  • 黒-黒-黒 なら2箇所切るの1通り
  • 黒-白-黒 なら白の左右どっちか切ればいいので2通り
  • (node, nodeを含めて上の区切られたエリアの中に黒があるかどうか) -> 何通り の木DPかな
    • (後から考えるとこれじゃ部分問題にならない... というか上ってどこまでだよ)
  • (root, なし)が答えかな
  • 今までどうだったかに応じて子ノードそれぞれつなぐべきか切るべきかとかで掛けたり足したりすれば良さそう
  • 式らしきものができた→実装→ sample 1 が合わない →(5分前) あ、分配できないのにできる前提でやってた
  • おわた
  • 状態は f(node, nodeとnodeの子以下の黒の個数) -> 何通り
  • で、ノードが白で黒の個数が1になるような時だけ全子ノードに対してさらにDPしている。
  • だいたい理解
  • いろいろ考えた結果
  • 状態を g(node, i, nodeとnodeのi番目までの子以下の黒の個数) -> 何通り
  • とするとさらに簡潔になりそう

i番目の子と node を含む領域をつなぐなら ---, 切るなら -|- として、

g(node, i, なし) は
  g(node, i-1, なし) × --- g(i番目の子, all, なし) ... OK
+ g(node, i-1, なし) × -|- g(i番目の子, all, なし) ... だめ(i番目の子以下が黒なしで確定しちゃう
+ g(node, i-1, なし) × --- g(i番目の子, all, あり) ... だめ
+ g(node, i-1, なし) × -|- g(i番目の子, all, あり) ... OK
+ g(node, i-1, あり) × --- g(i番目の子, all, なし) ... だめ
+ g(node, i-1, あり) × -|- g(i番目の子, all, なし) ... だめ(i番目の子以下が黒なしで確定しちゃう
+ g(node, i-1, あり) × --- g(i番目の子, all, あり) ... だめ
+ g(node, i-1, あり) × -|- g(i番目の子, all, あり) ... だめ

g(node, i, あり) は
  g(node, i-1, なし) × --- g(i番目の子, all, なし) ... だめ
+ g(node, i-1, なし) × -|- g(i番目の子, all, なし) ... だめ
+ g(node, i-1, なし) × --- g(i番目の子, all, あり) ... OK
+ g(node, i-1, なし) × -|- g(i番目の子, all, あり) ... だめ
+ g(node, i-1, あり) × --- g(i番目の子, all, なし) ... OK
+ g(node, i-1, あり) × -|- g(i番目の子, all, なし) ... だめ
+ g(node, i-1, あり) × --- g(i番目の子, all, あり) ... だめ
+ g(node, i-1, あり) × -|- g(i番目の子, all, あり) ... OK


なので
c0 = g(i番目の子, all, なし)
c1 = g(i番目の子, all, あり)
と置くと

g(node, i, なし) = g(node, i-1, なし) * (c0+c1)
g(node, i, あり) = g(node, i-1, なし) * c1 + g(node, i-1, あり) * (c0+c1)
となる.

初期条件は
g(node, 0, なし) = nodeが白 ? 1 : 0
g(node, 0, あり) = nodeが黒 ? 1 : 0

知りたい答えは g(root, all, あり)

  • Accepted in Practice room
  • 独立な状態を整理するとか、頭使う。。
VVI g;
VVI memo;
vector<bool> black;

// g(idx, all, b)
ll dfs(int prev, int idx, int b) {
	if(memo[b][idx]>=0) return memo[b][idx];

	// このノードと子ノード[0-i]以下からなる subtree を条件を満たすように区切ったとき, node を含む領域に含まれる黒の数 -> 何通り
	modll dp[2] = {1, 0}; // modll 略
	dp[0] = (modll)!black[idx];
	dp[1] = (modll)black[idx];
	REP(i, g[idx].size()) {
		int ni = g[idx][i];
		if(prev==ni) continue;
		ll c0 = dfs(idx, ni, 0);
		ll c1 = dfs(idx, ni, 1);
		modll n0 = dp[0] * (c0+c1);
		modll n1 = dp[0] * c1 + dp[1] * (c0+c1);
		dp[0] = n0;
		dp[1] = n1;
	}
	return memo[b][idx] = dp[b];
}

int main() {
	ll N,p,v;
	while(cin>>N) {
		g = VVI(N);
		memo = VVI(2, VI(N, -1));
		black = vector<bool>(N);
		REP(i, N-1) {
			cin>>p;
			g[i+1].PB(p);
			g[p].PB(i+1);
		}
		REP(i, N) {
			cin>>v;
			black[i] = v;
		}
		ll ans = dfs(-1, 0, 1);
		cout<<ans<<endl;
	}
	
	return 0;
}

2014-08-22

SRM 630 Div1 250 Egalitarianism3

13:50 |  SRM 630 Div1 250 Egalitarianism3 - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 630 Div1 250 Egalitarianism3 - kojingharangの日記  SRM 630 Div1 250 Egalitarianism3 - kojingharangの日記 のブックマークコメント

  • 木と各辺の距離が与えられる。集合に属するノード間の距離がすべて同じになるような最大集合のサイズを計算せよ。
  • 1≦|ノード数|≦50, 1≦距離≦1000
  • 1-2-3 として, 1と3は集合になりうるけど距離≧1なので 2 がある限り 1,2,3 はだめだな
  • 2を中心とする感じか
  • 必ず中心がありそうだ
  • 中心からの距離 r なものを集めるか→無向グラフのdfsを書き始める →r なもの同士が2rじゃないとだめだった
  • ていうかn≦50だからWFでいいじゃん (久しぶりに出たらやぼったいことをしてしまった)
  • submit
  • 中心があるのは答えが 3 以上になるときで、n≧2なら常に最低 2 になるんだった→再提出(やぼったい)
  • Failed system test
  • (あとで)
  • 2r のチェックが甘かった。各頂点について自分 + 距離が2rになる頂点数の最大値を k としてたが、うまくずれてて実際のサイズはもっと小さいという場合があり得る。
  • 追加しようとするたびに集合に加えていいか全部とチェックするようにした
  • 中心ノードから距離 r なクリークは複数通りあるんじゃないか?と思ったが結局1通りに定まるので条件を満たすものは貪欲に追加して良い。
  • ↓passed in practice room
class Egalitarianism3 {
	public:
	int maxCities(int n, vector <int> a, vector <int> b, vector <int> len) {
		if(n<=2) return n;
		VVI g = VVI(n, VI(n, 1<<30));
		REP(i, n-1) {
			g[a[i]-1][b[i]-1] = len[i];
			g[b[i]-1][a[i]-1] = len[i];
		}
		REP(i, n) g[i][i]=0;
		REP(k, n) REP(i, n) REP(j, n) g[i][j] = min(g[i][j], g[i][k]+g[k][j]);
		ll ans = 2;
		REP(i, n) {
			map<int, VI> hi; // hi[r] ... i を中心とした距離 r かつ全点間について距離が 2r のノード集合
			REP(j, n) {
				auto& vs = hi[g[i][j]];
				ll r = g[i][j];
				// check if we can add j to hi[r]
				bool ok = true;
				for(auto v : vs) if(g[v][j] != 2*r) ok=false;
				if(ok) {
					hi[r].PB(j);
					ans = max(ans, (ll)hi[r].size());
				}
			}
		}
		return ans;
	}
};
|