Hatena::Grouptopcoder

kojingharangの日記 RSSフィード

|

2014-05-23

SRM 621 Div1 500 TreesAnalysis

21:07 |  SRM 621 Div1 500 TreesAnalysis - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 621 Div1 500 TreesAnalysis - kojingharangの日記  SRM 621 Div1 500 TreesAnalysis - kojingharangの日記 のブックマークコメント

  • 頂点 0〜N-1 を持つ木が2つ与えられる。それぞれの木から1辺ずつ(e1, e2)選んで消すとそれぞれの木は2つの部分に分かれる。
  • それぞれの木からどちらかの部分を選んだ時、どちらの部分にも含まれる頂点ラベルの個数^2の最大値をS(e1, e2)とする。
  • 全ての辺に対して S(e1, e2) の和を求めよ。2≦頂点数≦4000
  • ファッ!?
  • なんてややこしいんだ
  • シミュレーションを書いてみる
  • それぞれの辺に対して頂点→片方の部分に含まれるかどうか bitset<4000> に持っておいて
  • 全ての e1, e2 について bitset の and, ~and, and~, ~and~ のbitを数える
  • O(N^2)だけど定数項が大きいのでだめそう
  • それしか思いつかないので提出 → challenge phase なぜか生き残る → failed system test
  • (あとで)

■ ポイント1

  • 0 を根だとして、頂点 n1 in Tree1, n2 in Tree2 に対して
  • 「n1 以下(n1とその子孫)の頂点ラベル集合 ∩ n2 以下の頂点ラベル集合」の個数 Xect[n1][n2] と
  • n1 以下の頂点の個数 size[木][n1] がわかっていれば答えが求まる
|n1以下の集合 ∩ n2以下の集合| = Xect[n1][n2]
|n1以下の集合以外 ∩ n2以下の集合| = size[Tree2][n2] - Xect[n1][n2]
|n1以下の集合 ∩ n2以下の集合以外| = size[Tree1][n1] - Xect[n1][n2]
|n1以下の集合以外 ∩ n2以下の集合以外| = (N - size[Tree1][n1]) - (size[Tree2][n2] - Xect[n1][n2])

n1以下以外  n2以下以外
 0 1 2      2 3 
n1以下     n2以下
 3 4        0 1 4

のとき Xect[n1][n2] = |{4}| = 1
n1以下以外 ∩ n2以下 は?
→ 0 1 4 のうち 4 は Xect に入ってるので n1 以下にあるので "n1以下以外 ∩ n2以下" には含まれない。
→ それ以外の 0 1 は n1 以下にはないはずなので n1 以下以外にあるはず→つまり n1以下以外 ∩ n2以下 に含まれる
なので size[Tree2][n2] - Xect[n1][n2]
  • まずこれが思いつかんのだなこれが。
  • size は dfs で求まる

■ ポイント2

  • Xect も size みたいに葉→根に足していって求められる。
  • まず初期状態として、n1 n2 に対して {n1}の頂点ラベル集合 ∩ {n2}の頂点ラベル集合 = XectNodeNode[n1][n2] を考える。
  • これは同じかどうかなので XectNodeNode[n1][n2] = n1==n2
  • n1以下の頂点ラベル集合 ∩ {n2}の頂点ラベル集合 = XectTreeNode[n1][n2] を求めるために Tree1 を dfs する。
  • XectTreeNode ← XectNodeNode として
  • 帰りがけ順に XectTreeNode[node][i] += XectTreeNode[child][i] for i in [0, N), Tree1 に node-childの辺がある
  • 次に Tree2 視点にするために XectNodeTree ← XectTreeNode の転置 として
  • n1以下の頂点ラベル集合 ∩ n2以下の頂点ラベル集合 = Xect を求めるために Tree2 を dfs
  • Xect ← XectNodeTree としておいて
  • 帰りがけ順に Xect[node][i] += Xect[child][i] for i in [0, N), Tree2 に node-childの辺がある
  • 最後、すべての辺に対してその辺が分断するノードの片方をもれなく列挙するとこも微妙に悩ましいが、
  • 0 を根としたのだから 0 以外の頂点は必ず親を持つ
  • → 0 以外の頂点 i すべてについて i とその親を結ぶ辺を列挙すれば MECE になるので頂点 [1, N)x[1, N) を全部見ればよい。
  • 全体で O(N^2)
  • accepted in practice room
  • ふむふむほうほう
  • ちなみに tourist さんは 13 分 30 秒で解いています。( ゚д゚)
// 理解した後に書いたコード (ほぼそのままですね
class TreesAnalysis {
public:
	void dfs(int t, int idx, int prev, vector<VVI>& g, VVI& size, VVI& xect) {
		int N = xect.size();
		size[t][idx]=1;
		REP(i, g[t][idx].size()) {
			int ci=g[t][idx][i];
			if(ci==prev) continue;
			dfs(t, ci, idx, g, size, xect);
			size[t][idx]+=size[t][ci];
			REP(j, N) xect[idx][j]+=xect[ci][j];
		}
	}
	long long treeSimilarity(vector <int> T1, vector <int> T2) {
		int N=T1.size()+1;
		vector<VVI> g(2, VVI(N));
		VVI size(2, VI(N));
		VVI xect(N, VI(N));
		REP(t, 2) REP(i, N-1) {
			int j=(t==0?T1:T2)[i];
			g[t][i].PB(j);
			g[t][j].PB(i);
		}
		REP(i, N) xect[i][i]=1;
		REP(t, 2) {
			dfs(t, 0, -1, g, size, xect);
			REP(i, N) RANGE(j, i+1, N) swap(xect[i][j], xect[j][i]);
		}
		ll ans = 0;
		RANGE(i, 1, N) RANGE(j, 1, N) {
			int x=xect[i][j];
			int a=size[0][i];
			int b=size[1][j];
			ll add = max(x, max(a-x, max(b-x, N-a-(b-x))));
			ans += add*add;
		}
		return ans;
	}
};

2014-04-25

SRM 618 Div1 250 Family

12:30 |  SRM 618 Div1 250 Family - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 618 Div1 250 Family - kojingharangの日記  SRM 618 Div1 250 Family - kojingharangの日記 のブックマークコメント

  • 親ノードが0個か2個なDAGが与えられる。ノードに男か女ラベルを割り当てたい。親ノードがある場合は必ず男女の組み合わせにできるかどうかを求めよ。
  • 1≦ノード数≦100
  • 親がいるならそれらのラベルは異なっている必要がある。
  • これを、全ノードについて、ノードID→それと異なるべきノードIDという形で覚えておく
  • node i に対して異なるべきノードたちは全て同じラベルである必要がある。ラベルが2種類なので。
  • 同じであるべきものを union find たんでまとめる
  • 再度異なるべきものを見て、それらが同じラベルということになってたら矛盾なので Impossible
  • 矛盾してなければ Possible
  • accepted
  • コードを書き終わってコンパイルしたらコンパイルエラーもなく1回で全テスト通ったのでそのままsubmitした。なかなかない事なのでとても気持ちよかった。(*´~`*)
class Family {
	public:
	string isFamily(vector <int> P1, vector <int> P2) {
		int N=P1.size();
		VVI diff(N, VI());
		REP(i, N) {
			if(P1[i]==-1) continue;
			diff[P1[i]].PB(P2[i]);
			diff[P2[i]].PB(P1[i]);
		}
		UnionFind uf(N);
		REP(i, N) {
			REP(j, diff[i].size()) {
				uf.Union(diff[i][0], diff[i][j]);
			}
		}
		REP(i, N) {
			REP(j, diff[i].size()) {
				if(uf.Find(i, diff[i][j])) return "Impossible";
			}
		}
		return "Possible";
	}
};

2014-04-11

SRM 616 Div1 500 ColorfulCoins

12:53 |  SRM 616 Div1 500 ColorfulCoins - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 616 Div1 500 ColorfulCoins - kojingharangの日記  SRM 616 Div1 500 ColorfulCoins - kojingharangの日記 のブックマークコメント

  • 最大60種類のコインの額面が与えられる。コインには種類ごとに異なる色が塗ってある。額面は見た目では分からない。
  • ATMがあって数値をいれると対応する額のコインが最小枚数でじゃらじゃら出てくる。
  • 全種類のコインの色を特定するのに最低何回ATMから引き出せばいいか求めよ。
  • 1≦額面≦10^18, 額面は単調増加で与えられて最初は1, どの2つの額面も割り切れる
  • 複雑だ....
  • (いろいろ考察)
  • 1 10 100 1000 だったら、4321円引き出せば1が1枚, 10が2枚, 100が3枚, 1000が4枚もらえるので1回で特定できる。
  • 1 2 4 8 だと、8は何枚でも出せるけど 1 2 4 は 1枚までしかもらえないので困る。
  • 1回の引き出しで出せるコインの枚数は 額面/直前の額面-1 まで
  • 特定できるとはどういうことか?
  • あるコインについて、1〜N回目に取る枚数をN要素ベクトルだと思うと、要素が1個でも異なるなら識別できる。
  • (i回目にvec[i]枚出てきたような色は vec が異なれば一意に定まる)
  • 出せるコインの枚数が K なら取り方は (K+1)^N 通りある。
  • が、全部0の場合そもそも何色か分からないので、特定に役立つ組み合わせは (K+1)^N-1通り。(書いて実行するまで気付かなかった)
  • 基本的には、出せるコインの枚数が K であるようなコインの種類数が (K+1)^N-1 以下なら識別できそう。
  • 出せるコインの枚数が K 枚未満のやつは?→ (K+1)^N 通りの中に必ず含まれるので、その分余裕を減らさないといけない。
  • というわけで、すべての K について
  • 「出せるコインの枚数が K 以下であるようなコインの種類数 ≦ (K+1)^N-1」
  • なら N 回で全種類識別可能。
  • 1枚ずつ取っていけば最悪でも60回で全部識別できるので N in [1, 60] を全部試す。
  • 方針を思いついたのが終了13分前
  • 全部 0 の場合を -1 するのに気づいてサンプル通って提出したのが終了2分前
  • ぎりぎりセーフ
例: 1  2  4  12  24 の場合

額   1回で何枚までとれるか
 1   1
 2   1
 4   2
12   1
24   ∞

額  最大  1,2 回目に何枚とるか(例)
 1    1    1,0
 2    1    0,1
12   1    1,1 // ここまで (1+1)^2-1 == 3 種類以下ならOK
 4    2    2,0 // ここまで (2+1)^2-1 == 8 種類以下ならOK
243,0 // ここまで (大きな値+1)^2-1 種類以下ならOK
  • accepted
  • (実際には 60<2^6なので最大でも6回で識別できるようです)
ll po(ll a, ll b) {
	ll v=1;
	REP(i, b) {
		v*=a;
		if(v>10000) return 10000; // 1, 2, 3, たくさん の精神
	}
	return v;
}

class ColorfulCoins {
	public:
	int minQueries(vector<long long> V) {
		int N=V.size();
		map<ll, ll> hi;
		RANGE(i, 1, N) {
			ll v = V[i]/V[i-1];
			hi[v]++;
		}
		hi[1LL<<60]++;
		cout<<hi<<endl;
		RANGE(n, 1, N+1) {
			ll used=0;
			int ok=1;
			FOR(e, hi) {
				ll cap = po(e->first, n) - 1 - used;
//				cout<<"C "<<cap<<" "<<e->first<<" "<<e->second<<endl;
				if(cap < e->second) {ok=0;break;}
				used += e->second;
			}
			if(ok) return n;
		}
		return -1;
	}
};

2014-03-29

SRM 614 Div1 250 MinimumSquare

03:22 |  SRM 614 Div1 250 MinimumSquare - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 614 Div1 250 MinimumSquare - kojingharangの日記  SRM 614 Div1 250 MinimumSquare - kojingharangの日記 のブックマークコメント

  • 2Dの整数点座標が2〜100個与えられる。整数座標のAABBな正方形のうち点をK個以上含む(辺上は含まない)ものの最小の面積を求めよ。
  • 1≦K≦点の個数
  • 正方形に含むべき X の範囲 [ X[x0], X[x1] ] を決めて、X座標がその範囲に入ってるY座標を列挙する。
  • そのY座標たちをソートして Y 座標の範囲を点が K 個入るように Y[i], Y[i+K-1] と決める。
  • そしたら K 個以上含む長方形の幅高さ W, H が出るので max(W, H) が正方形の1辺。
  • challengeされる...

あとで

  • 何も考えずに当然のように x1 in [x0+1, N) としていた。
  • x1 in [x0, N) にしないとK=1の時とかにポシャる。
  • くやしけり!
  • accepted in practice
class MinimumSquare {
	public:
	long long minArea(vector <int> x, vector <int> y, int K) {
		int N=x.size();
		vector<int> X(x);
		sort(ALL(X));
		ll ans = 1LL<<60;
		//REP(x0, N) RANGE(x1, x0+1, N) { // だめじゃった
		REP(x0, N) RANGE(x1, x0, N) {
			ll W = abs(X[x0]-X[x1])+2;
			VI Y;
			REP(i, N) if(X[x0]<=x[i] && x[i]<=X[x1]) Y.PB(y[i]);
			sort(ALL(Y));
			int M=Y.size();
			REP(i, Y.size()) {
				if(i+K-1>=M) break;
				ll H = abs(Y[i]-Y[i+K-1])+2;
				ans = min(ans, max(W, H));
			}
		}
		return ans*ans;
	}
};

2014-03-23

SRM 613 Div1 250 TaroFriends

00:38 |  SRM 613 Div1 250 TaroFriends - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 613 Div1 250 TaroFriends - kojingharangの日記  SRM 613 Div1 250 TaroFriends - kojingharangの日記 のブックマークコメント

  • 直線上の猫の座標 C[i] が最大 50 個与えられる。各自 +X か -X にゃー(動く)。一番左の猫と一番右の猫の座標の差の最小値を求めよ。
  • |C[i]|≦10^8, 0≦X≦10^8
  • 一番左(または右)の猫の座標は最大でも100通りしかないのがポイント。
  • 一番左の座標 left を決めたら、全ての猫を left を下回らないようにできるだけ左に動かせばいい。
  • 左がだめなら右に動かす。それもだめならその left はだめ。
  • 置けた left の中で差が最小のものが答え。
  • 毎回こんな 250 ならいいのに。
  • accepted
class TaroFriends {
	public:
	int getNumber(vector <int> C, int X) {
		ll ans = 1<<30;
		REP(i, C.size()) {
			REP(j, 2) {
				int ok=1;
				ll lans = 0;
				ll left = C[i] + (j?-X:X);
				REP(k, C.size()) {
					if(left <= C[k]-X) lans=max(lans, abs(C[k]-X - left));
					else if(left <= C[k]+X) lans=max(lans, abs(C[k]+X - left));
					else ok=0;
				}
				if(ok) ans=min(ans, lans);
			}
		}
		return ans;
	}
};
|