Hatena::Grouptopcoder

kojingharangの日記 RSSフィード

|

2012-05-21

SRM 453 Div1 500 EllysRivers

20:22 | SRM 453 Div1 500 EllysRivers - kojingharangの日記 を含むブックマーク はてなブックマーク - SRM 453 Div1 500 EllysRivers - kojingharangの日記 SRM 453 Div1 500 EllysRivers - kojingharangの日記 のブックマークコメント

  • greedy解がなぜうまくいくのか分からなかったので考えてみた
  • 「(x, ?) から (x+width, ?+y) に行く時に比べて (x+width, ?+y+1) に行く時ではどれだけ長くなるか」を各 x, y について求めておく
  • 上記は、x が固定なら y が大きいほど大きい
>>> import math
>>> [ math.hypot(3,y+1)-math.hypot(3, y) for y in range(10) ]
[0.16227766016837952, 0.4432736152956096, 0.6370894116552956, 0.7573593128807152, 0.8309518948453007, 0.8772520376540687, 0.9075691733645392, 0.9282306394536217, 0.9428292351876078, 0.9534735284054126]
  • この数列の 0〜Y の和は、「(x, ?) から (x+width, ?) に行く時に比べて (x+width, ?+Y) に行く時ではどれだけ長くなるか」なので、
  • x → x+width と移動するときに、同時に Y 個上に移動するときの増加コストと同じ
  • この数列の小さい方から Y 個の和は、0〜Y の和と等しい (ポイント)
  • なので、全部の差分をソートして小さい方から length 個足せば、全部で length 個上にいくときの最小増加分がわかる
  • ということみたいだ。すげ〜

2012-05-19

SRM 453 Div1 250 EllysXors

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

  • L~R の XOR を求める問題。1 <= L, R <= 4,000,000,000
  • 1~2**N-1 の XOR は 0 になるっぽい
  • L~R の XOR は 1~L-1 の XOR と 1~R の XOR の XOR になるっぽい
  • なので、1~n の XOR を求める f(n) を作る
  • 最上位ビットを見つけて 2**N <= n < 2**(N+1) な N を見つけたら、2**N より小さい範囲の XOR は 0
  • 2**N から n までは、最上位ビットに関しては n - 2**N の偶奇で XOR の結果がわかる
  • それと f(最上位ビットを消した値) の XOR を再帰的にとる
  • あれ f(2) が 3 になるぞ 大きな値で試したりして、よくわからないけど 2 のときだけこうなるっぽい
  • 10未満のときは定義通り計算することにする
  • いろいろ証明できてないので100までで定義通りの計算結果と一致することを確認
  • passed system test
ll ff(ll v) {
	ll ans=0;
	for(ll i=1;i<=v;i++) ans^=i;
	return ans;
}

ll f(ll v) {
	if(v<10) return ff(v);
	ll a=1;
	while(v>a) a=a<<1;
	a>>=1;
	return (((v-a)&1) ? 0 : a) ^ f(v^a);
}

class EllysXors {
	public:
	long long getXor(long long L, long long R) {
		//REP(i, 100) if(f(i+1)!=ff(i+1)) cout<<"ERRO"<<endl;
		return f(R)^f(L-1);
	}
};
  • ちなみに unsigned int にして単純なループで通してる人がいた。
  • 手元で実行したら 3400ms くらい。
  • ほほ~
ll fff(ll A, ll B) {
	unsigned ans=0;
	unsigned AA = A;
	unsigned BB = B;
	for(unsigned i=AA;i<=BB;i++) ans^=i;
	return ans;
}

2012-05-18

Codeforces div2 190d - Non-Secret Cypher

22:46 |  Codeforces div2 190d - Non-Secret Cypher - kojingharangの日記 を含むブックマーク はてなブックマーク -  Codeforces div2 190d - Non-Secret Cypher - kojingharangの日記  Codeforces div2 190d - Non-Secret Cypher - kojingharangの日記 のブックマークコメント

  • 数値 W[i], 0 <= i < N, N <= 4*10^5 が与えられる。0 <= i <= j < N な区間 [i, j] のうち同じ数字が K 個以上含まれるものの個数を求める。
  • なんの DP? と思ったけど N が大きいので違うっぽい
  • しゃくとり法
  • 数値ごとに個数をカウントして最大のものが K になるまで区間の終わりを右にずらす
  • K になったら、区間の終わり以降のすべての終わりポイントにて K 個以上なので終わりポイントの個数を足す
  • 最大のものが K 未満になるまで区間の始まりを右にずらす。その間、同様に終わりポイントの個数を足す
  • O(N)
  • 本番では ans が int になってて WA. N(N+1)/2 なので最大 8*10^10 程度になるのだった。
  • なんかもう全ての整数変数は long long でいいんじゃないかって気がしてくる

↓あとで Accepted

int main() {
	int N, K;
	cin>>N>>K;
	VI w(N);
	REP(i, N) cin>>w[i];
	
	int L=0, R=0; // [L, R)
	int maxi = -1;
	int maxv = 0;
	map<int, int> hi;
	ll ans = 0;
	
	while(1) {
		if(R==N) break;
		while(R<N && maxv<K) {
			R++;
			hi[w[R-1]]++;
			if(hi[w[R-1]] > maxv) {
				maxv = hi[w[R-1]];
				maxi = w[R-1];
			}
		}
		if(maxv<K) break;
		ans += N-R+1;
		//cout<<L<<" "<<R<<"  + "<<N-R+1<<endl;
		while(L<N-1 && maxv == K) {
			L++;
			hi[w[L-1]]--;
			if(w[L-1]==maxi) {maxv--;break;}
			ans += N-R+1;
		}
		//cout<<L<<" "<<R<<endl;
	}
	
	cout<<ans<<endl;
	return 0;
}

2012-05-12

TCO2012 Round2B - 300 BlurredDartboard

03:43 |  TCO2012 Round2B - 300 BlurredDartboard - kojingharangの日記 を含むブックマーク はてなブックマーク -  TCO2012 Round2B - 300 BlurredDartboard - kojingharangの日記  TCO2012 Round2B - 300 BlurredDartboard - kojingharangの日記 のブックマークコメント

  • p[i] には 1~N(<= 50) のどれかが重複なく入っていたが、いくつか 0 になっている。適当な p[i] を選んで加算するのを K 回行う。同じのを複数回選んでいい。合計が確実に P(<= 10**9) 以上になるような最小の K を求める問題。
  • 0 のやつを全部加算すると、隠れてる数の合計が確実に加算されることになる
  • なので、まずできるだけ max(0 のやつの合計, 0 じゃないやつの最大値) を使う。
  • 残りは, 0 じゃないやつの最大値を使うか、0 のやつで最悪の選び方をしたとして小さい方から順に使うかのどちらか。
class BlurredDartboard {
	public:
	int minThrows(vector <int> p, int P) {
		int N=p.size();
		
		VI vis(N, 0);
		int ma=-1;
		REP(i, N) {
			if(p[i]>0) {
				vis[p[i]-1]=1;
				ma=max(ma, p[i]);
			}
		}
		VI hid;
		REP(i, N) if(vis[i]==0) hid.PB(i+1);
		int hidsum = accumulate(ALL(hid), 0);
		sort(ALL(hid));
		
		int ans=0;
		if(ma==-1 || hidsum > ma*hid.size()) {
			ans += P/hidsum*hid.size();
			P -= P/hidsum*hidsum;
		} else {
			ans += P/ma;
			P -= P/ma*ma;
		}
		cout<<ans<<" "<<P<<endl;
		if(P==0) return ans;
		
		int lans=0;
		int PP=P;
		REP(i, hid.size()) {
			PP-=hid[i];
			lans++;
			if(PP<=0) break;
		}
		if(PP>0) lans = 10000;
		cout<<lans<<endl;
		if(ma!=-1) lans = min(lans, (P+ma-1)/ma);
		cout<<lans<<" "<<ma<<endl;
		
		return ans+lans;
	}
};

TCO2012 Round2B - 550 HeavyBooks

03:43 |  TCO2012 Round2B - 550 HeavyBooks - kojingharangの日記 を含むブックマーク はてなブックマーク -  TCO2012 Round2B - 550 HeavyBooks - kojingharangの日記  TCO2012 Round2B - 550 HeavyBooks - kojingharangの日記 のブックマークコメント

  • ICPC world final の時事ネタw
  • 本(50冊まで)とその重さが与えられる。TがM[0]冊選んで W にあげて、その中からWがM[1]冊をWからTに移して、TがM[2]冊をTからWに移して...を交互に繰り返す。最大M[50]まで。
  • Tは重さW-Tを最大化したい。Wは重さT-Wを最大化したい。最適に行動した場合の最終的な T, W の本の重さを求める問題。ただし複数同じだったら重さ T+W を最大化する。
  • 残り40分くらい。
  • 選び方は 50 C M[0] あるので賢くやる必要がある
  • 最初に M[0] 個選んでしまえば、あとはお互いに一番重いものから M[i] 個渡せばいい。
  • なので、最初に選んだ本の重さをソートして番号をつけたとして、最後にどっちが何番目の本を持ってることになるかシミュレートできる。
  • という状況下で、W-T が最大になるようにソート済みの本とソート済みのM[0]冊の本を対応付ければいい。
  • どっちもソートしてあるので、M[0] 個を左から順に見ていって「今の本と対応付ける場合」と「次の本と対応付ける場合」で都合の良い方を再帰的に選ぶ。
  • 対応は最初の本の数 * M[0] 通りなのでメモ化再帰でOK
  • 値は、W-T, W+T のペアにすれば max で自然にいける。W-T, W+T が揃ってるので値の復元も足したり引いたりすればいい。
  • という方針で書いたけどサンプルが通らなくて直してるうちに終了。
  • (あとで)
  • 方針は合ってたけど invalid を表す充分に小さな値が充分小さくなかったり終了条件が1コずれてたり値の復元の正負が逆だったりして良くない。最初に書く時に正確に書くべし。

↓あとで通したやつ

vector <int> BB;
VI w;

map<PII, PII> memo;
PII f(int bi, int wi) {
	PII key = MP(bi, wi);
	if(memo.count(key)) return memo[key];
	
	if(wi==w.size()) return MP(0, 0);
	if(bi==BB.size()) return MP(-100000000, -100000000);
	
	PII rest = f(bi+1, wi+1);
	if(w[wi]==0) rest.first-=BB[bi];
	else         rest.first+=BB[bi];
	rest.second+=BB[bi];
	
	PII restB = f(bi+1, wi);
	//cout<<bi<<" "<<wi<<" "<<rest<<" "<<restB<<endl;
	return memo[key] = max(rest, restB);
}

class HeavyBooks {
	public:
	vector <int> findWeight(vector <int> B, vector <int> M) {
		memo = map<PII, PII>();
		sort(ALL(B));
		
		w = VI(M[0], 1);
		int to=0;
		REP(i, M.size()-1) {
			int mo = M[i+1];
			for(int j=M[0]-1;j>=0;j--) {
				if(w[j]==1-to) {
					w[j]=to;
					mo--;
					if(mo==0) break;
				}
			}
			to=1-to;
		}
		cout<<w<<endl;
		BB=B;
		PII r = f(0, 0);
		cout<<r<<endl;
		vector<int> ans(2);
		ans[0]=-(r.first-r.second)/2;
		ans[1]=(r.first+r.second)/2;
		return ans;
	}
};

2012-05-09

SRM 542 - 250 PatrolRoute

21:36 |  SRM 542 - 250 PatrolRoute - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 542 - 250 PatrolRoute - kojingharangの日記  SRM 542 - 250 PatrolRoute - kojingharangの日記 のブックマークコメント

  • 2次元整数座標の範囲(x, y) ただし x in [0, X), y in [0, Y) から x または y が異なる3点を選ぶ。3点をマンハッタン距離で巡回するときの合計距離が [minT, maxT] に収まるような選び方は何通りあるか。
  • DP? と思ったけど、範囲が1増えて新しいとこから点を選ぶと距離とか変わっちゃうし、なんかうまくいかなさそう
  • 3点のbounding box(minX, minY)-(maxX, maxY)について幅 W 高さ H とすると、合計距離は 2(W+H) になることに気づく(ここがポイントだった)
  • bounding box (以下BB)の大きささえ決まってしまえば合計距離が決まる。それが minT~maxT かはわかる
  • BB は (0, 0)-(X-1, Y-1) の範囲には (X-W)(Y-H) 通りずらして置ける
  • BB 内での点の置き方は...
  • 対角線に2つ置いてあとはその中 (W-1)(H-1) に置くパターンが180度回転で一致するので2通り
  • 左上の点と、右の辺(H-1)と下の辺(W-1)に1こずつ置くパターンが回転すると4通り
  • というわけで BB の幅高さ全通りに対して, minT~maxT なら 6(X-W)(Y-H)(W-1)(H-1) の合計が答え
#define MOD ((ll)1000000007)

class PatrolRoute {
	public:
	int countRoutes(int X, int Y, int minT, int maxT) {
		ll ans = 0;
		for(int W=2;W<X;W++) {
			for(int H=2;H<Y;H++) {
				if( minT <= 2*(W+H) && 2*(W+H) <= maxT ) {
					ll a = (X-W) * (Y-H);
					ll b = (W-1)*(H-1)*6;
					ll lans = a*b;
					//cout<<W<<" "<<H<<" "<<a<<" "<<b+c<<endl;
					ans = (ans + lans) % MOD;
				}
			}
		}
		return ans;
	}
};

SRM 542 - 500 StrangeDictionary2

21:36 |  SRM 542 - 500 StrangeDictionary2 - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 542 - 500 StrangeDictionary2 - kojingharangの日記  SRM 542 - 500 StrangeDictionary2 - kojingharangの日記 のブックマークコメント

  • 最大16個の文字列(最大50文字)のそれぞれをランダムな permutation で並び替えた後に文字列同士をソートしたとき、元の文字列ごとに先頭に来る確率を求める問題
  • 残り37分くらい
  • 最小値が1コになるまで permutation 後の先頭の文字から見ていく DFS かと思って実装したけど時間切れ
  • 50! だけど最小値はわりと早く1コに定まるんじゃないか、あと深くの方にいくと許容誤差以内になるからやんなくていいんじゃないかと思ったけどはて
  • bitDPとかいう声があるけど理解できん。解説とか見る
|