Hatena::Grouptopcoder

kojingharangの日記 RSSフィード

|

2013-01-23

SRM 567 Div1 500 StringGame

19:08 |  SRM 567 Div1 500 StringGame - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 567 Div1 500 StringGame - kojingharangの日記  SRM 567 Div1 500 StringGame - kojingharangの日記 のブックマークコメント

  • 同じ長さの文字列集合Sが与えられる。自分は1コ(xとする)を選んで文字を並び替えて X とする。さらにアルファベットの順序を決める。相手は x 以外について文字を好きに並び替える。相手がどう並び替えても X が辞書式順序で最小になる(勝つ)ような x のインデックスを全て求める。
  • 文字列の長さ≦50, 2≦|S|≦50, 文字はa~z
  • 観察: xとアルファベットの並び順が決まるとXは一通りに定まる。(その並び順でxをソートしたものがXになる)
  • ので、どういう並び順にしたら勝つるかを考えればいい。
  • x の候補それぞれについて、それに含まれるある文字 c の個数が他の全ての文字列における c の個数より多い場合、
  • cを先頭に定めれば勝つる、そうじゃない場合は負ける可能性があるのでだめ、というコードを書いてあっさり challenge される。
  • (あとで)
  • hotpepsiさんやsimezi_tanさんのを見る
  • 自分が書いたやつは勝利の十分条件でしかなくて、実はもっと勝てる場合があるみたい。
  • "aabcdd", "aabbcd", "aabbcc" の場合、文字カウントは
a b c d
-------
2 1 1 2
2 2 1 1
2 2 2 0
  • なので、明らかに他より多い文字があるというやつは 0, 2 しかない
  • が、1 はアルファベット順を "bdac" にすれば勝てる

  • というわけで、アルファベットの順序を順々に決めていく方針でやってみる
  • a-zについて、
  • (1)他の文字列に多く含まれるような文字を採用すると自分が負け確定になるなのでだめ
  • (2) そうでない場合、次のアルファベットとしてその文字を採用すれば、その文字の個数が自分のより少ない他の文字列を負けにできるのでそうする
  • 自分が負け確定になるのでだめだと思っていたアルファベットを含む文字列は、実は別のa-zについて(2)で負けになってるかもしれない。ので、再度(1)を評価する。具体的には情報が伝搬するまで(1)(2)を最大 26 回繰り返す
  • 自分以外が負け確定になってたら自分が勝てるので答えに追加する
  • というアルゴリズムでよいみたい。

↓あとで (accepted in practice)

class StringGame {
	public:
	vector <int> getWinningStrings(vector <string> S) {
		int N=S.size();
		VVI hi(N, VI(26));
		REP(i, N) {
			REP(j, S[i].size()) {
				hi[i][S[i][j]-'a']++;
			}
		}
		cout<<hi<<endl;
		vector<int> ans;
		REP(i, N) {
			string alpha;
			VI live(N, 1);
			live[i]=0;
			REP(loop, 26) {
				REP(c, 26) {
					int lose_char=0;
					REP(j, N) {
						if(live[j] && hi[j][c] > hi[i][c]) lose_char=1;
					}
					if(lose_char) continue;
					
					REP(j, N) {
						if(live[j] && hi[j][c] < hi[i][c]) {
							live[j]=0;
							// アルファベットの順番(のうち確定したものだけ)も出すようにしてみる
							if(alpha.find(c+'a')==string::npos) alpha.PB(c+'a');
						}
					}
				}
			}
			//cout<<live<<endl;
			cout<<i<<" "<<(accumulate(ALL(live), 0LL)==0 ? "OK":"NG")<<" Alphabet: "<<alpha<<endl;
			if(accumulate(ALL(live), 0LL)==0) ans.PB(i);
		}
		return ans;
	}
};


  • 出力例
"aabcdd", "aabbcd", "aabbcc" の場合

0 OK Alphabet: d
1 OK Alphabet: bd
2 OK Alphabet: bc

2013-01-21

SRM 567 Div1 250 TheSquareRootDilemma

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

  • 1≦A≦N, 1≦B≦M に対して (√A+√B)^2 が整数になるような (A, B) の個数を求める。
  • N, M ≦77777
  • (√A+√B)^2 == A+B+2√A√B なので √A√B が整数ならいい
  • p0, p1, ... を素数として
  • A == p0^ap[0] * p1^ap[1] * ...
  • B == p0^bp[0] * p1^bp[1] * ...
  • として、各指数の偶奇が一致していればいい (ap[i]%1 == bp[i]%1 for all i)
  • √77777 == 278.8.. だし 290 までの素数の係数を記録すればいいか (→まちがい
  • B それぞれについて偶奇を ll に詰め込んでその値から個数への map を作っておいて
  • A それぞれについて B の map を引いたものの個数を足してけばいい
  • → failed system test
  • 割ってく素数の最大値は 277 でいいけど、素因数自体は普通に 290 より大きなのがあるじゃんか (よわい
  • ll に詰め込んだ偶奇ビットに加えて 290 以下の素数で割った後の数(1 or 大きな素数)も記録するようにしたところ OK

↓あとで (accepted in practice)

VI make_primes(int N) { // return all prime numbers less than N  memory: O(N) time: O(N^2)?
	VI p(N, 1), out;
	for(int i=2;i<N;i++) if(p[i]) { out.push_back(i); for(int j=i*2;j<N;j+=i) p[j]=0; }
	return out;
}

VI primes;
pair<ll, ll> f(int n) {
	ll out = 0LL;
	REP(ii, primes.size()) {
		int i=primes.size()-1-ii;
		int odd = 0;
		while(n % primes[i] == 0) {
			n/=primes[i];
			odd = 1 - odd;
		}
		out = (out << 1) | odd;
	}
	return MP(n, out);
}

class TheSquareRootDilemma {
	public:
	int countPairs(int N, int M) {
		primes = make_primes(290);
		
		map<pair<ll, ll>, ll> ms;
		RANGE(i, 1, M+1) {
			ms[f(i)]++;
		}
		ll ans = 0;
		RANGE(i, 1, N+1) {
			ans += ms[f(i)];
		}
		return ans;
	}
};

2013-01-20注意喚起の件 このエントリーを含むブックマーク このエントリーのブックマークコメント

みなさまにおかれましては各種コンテスト前の Register をお忘れにならぬようご注意くださいませ

2012-12-08

SRM 563 Div1 300 FoxAndHandle

04:57 |  SRM 563 Div1 300 FoxAndHandle - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 563 Div1 300 FoxAndHandle - kojingharangの日記  SRM 563 Div1 300 FoxAndHandle - kojingharangの日記 のブックマークコメント

  • 文字列Sが与えられる。H と H のpermutationをそれぞれ順番を変えずに織り交ぜたとき S になるような辞書順最小の H を求める問題
  • |S|≦50
  • 辞書順最小ということで、なんとか H を 1 文字ずつ後戻りせずに決めていきたい
  • H の次の文字としてありうる文字集合を小さい順に試すとして...
  • H の permutation なら順番はどうでもいいので、H が S の中に順番通りに入っていればそれでいい
  • 順番を保証するために、H の次の文字を試す時は S[Si] 以降を見ることにする
  • S[Si] 以降で H の次の文字候補の出現位置を探す。
  • その位置以降に H の残り文字集合がすべて含まれていればその候補は OK
  • 予めhi[i][c]にS[i]以降の文字カウントを保存しておいたけど毎回計算してもよかった
  • fedcbafedcba の場合
  • H の次の文字候補 abcdef
  • a を試すとして fedcbafedcba から a を検索 → afedcba
  • afedcba に abcdef が入るので1文字目は a で確定
  • H の次の文字候補 bcdef
  • b を試すとして afedcba から b を検索 → ba
  • ba には bcdef は入らないので b はだめ
  • c~eもだめ
  • fedcba に abcdef が入るので2文字目 f で確定
  • ...

  • で、Si の進め方が 1 足りなくて failed system test. なんとも.

↓あとで (accepted in practice)

class FoxAndHandle {
	public:
	string lexSmallestName(string S) {
		int N=S.size();
		VVI hi(N+1, VI(256));
		REP(i, N) {
			hi[N-1-i] = hi[N-1-i +1];
			hi[N-1-i][S[N-1-i]]++;
		}
		string Hs;
		{
			VI w(256);
			REP(i, N) w[S[i]]++;
			REP(i, 256) {
				while(w[i]) {
					Hs.PB((char)i);
					w[i]-=2;
				}
			}
		}
		string H;
		int Si = 0;
		while(Hs.size()) {
			VI lhi(256);
			REP(j, Hs.size()) lhi[Hs[j]]++;
			
			REP(k, Hs.size()) {
				int idx=0;
				RANGE(i, Si, N) if(S[i]==Hs[k]) {idx=i;break;}
				
				int ok=1;
				REP(j, 256) if(hi[idx][j]<lhi[j]) ok=0;
				
				if(ok) {
					H.PB(Hs[k]);
					string nHs;
					int removed=0;
					REP(z, Hs.size()) {
						if(!removed && Hs[k]==Hs[z]) removed=1;
						else nHs.PB(Hs[z]);
					}
					Hs=nHs;
					//Si=idx; // だめ
					Si=idx+1; // OK
					break;
				}
			}
		}
		return H;
	}
};

2012-12-07

Codeforces #153 Div1 A - Points on Line

12:44 |  Codeforces #153 Div1 A - Points on Line - kojingharangの日記 を含むブックマーク はてなブックマーク -  Codeforces #153 Div1 A - Points on Line - kojingharangの日記  Codeforces #153 Div1 A - Points on Line - kojingharangの日記 のブックマークコメント

  • X座標が N 個与えられるので、点間の最大距離が D 以下になるような 3 点の取り方の数を求める問題。
  • N≦10**5, D≦10**9
  • 両端が決まるとそれに挟まれる別の点の個数だけ 3 点とれる
  • ソートしておくと...
  • 左端それぞれについて(O(N)), それに対応する右端のうち一番右のところは upper_bound で O(logN) で見つかる
  • 右端としてありうるのはX[左端+2]からX[右端]までなので
  • ある左端についての答えは M=左端と一番右の右端に挟まれる数字の個数として M≧0 なら 1からMまでの和
  • 全体で O(NlogN)
  • (あとで)しゃくとり法みたいにすると upper_bound 使わなくてよいようです
  • ↓Accepted
int main() {
	//ios::sync_with_stdio(false);
	ll N, D;
	while(cin>>N>>D) {
		VI w(N);
		REP(i, N) {
			cin>>w[i];
		}
		sort(ALL(w));
		
		ll ans=0;
		REP(i, N-2) {
			// binary search [i+1, N-1]
			int idx = (upper_bound(&w[i+1], &w[N], w[i]+D) - &w[0])-1;
			//cout<<i<<" "<<idx<<endl;
			ll mid = idx-i-1;
			if(mid>=0) ans += mid*(mid+1)/2;
		}
		
		cout<<ans<<endl;
	}
	
	return 0;
}


Codeforces #159 Div1 B - Playing with Permutations

12:44 |  Codeforces #159 Div1 B - Playing with Permutations - kojingharangの日記 を含むブックマーク はてなブックマーク -  Codeforces #159 Div1 B - Playing with Permutations - kojingharangの日記  Codeforces #159 Div1 B - Playing with Permutations - kojingharangの日記 のブックマークコメント

  • 置換 A を Ai in [1, N] (i in [1, N]), Ai≠Aj (i≠j, i, j in [1, N]) とする
  • 置換 Q, S が与えられる。恒等置換 P に対して毎回 Q か Q^-1 を作用させる操作を K 回やる。
  • 「0回目~K-1回目は P!=S だが、ちょうど K 回目で S に等しくなる」ということはありえるかどうか求める問題。
  • N, K≦100
  • * を作用させる演算子として、P * Q * Q^-1 == P と元に戻るのと、作用させる順番が関係ないので、K 回後の状態は P * Q^? とあらわせる
  • なので、i<K 回目はすべての場合で P!=S で i==K 回目で P==S となる場合があるときだけ YES とするようにしてみたけど sample があわない
  • 途中で P==S になる場合があってもそこを通らずに K 回目で P==S となれば YES になるのか
  • 結局 BFS をすることになった。
  • 予め Same[j] = S == Q^j (j in [-K, K]) かどうかを計算しておいて、
  • ノード i が Same じゃなければ i-1, i+1 をキューに追加
  • K回目で Same なら YES
  • ノード数は 2K+1 程度なので O(NK+K^2) くらい?

  • ↓Accepted
ll N, K;
VI Q, S, P, W;
VI Same; // idx [-K K]  [0 2K]

int apply(int sign) {
	if(sign==1) {
		REP(i, N) W[i]=P[Q[i]-1];
	} else {
		REP(i, N) W[Q[i]-1]=P[i];
	}
	REP(i, N) P[i]=W[i];
	return 1;
}

int main() {
	//ios::sync_with_stdio(false);
	while(cin>>N>>K) {
		Q=VI(N);
		S=VI(N);
		P=VI(N);
		W=VI(N);
		Same=VI(2*K+1);
		REP(i, N) {
			cin>>Q[i];
		}
		REP(i, N) {
			cin>>S[i];
		}
		
		for(int sign=-1;sign<=1;sign+=2) {
			REP(i, N) P[i]=i+1;
			REP(i, K+1) {
				int same=1;
				REP(j, N) if(P[j]!=S[j]) same=0;
				//cout<<P<<endl;
				Same[i*sign + K] = same;
				apply(sign);
			}
		}
		//cout<<Same<<endl;
		//break;
		
		deque<int> q;
		q.PB(0);
		int OK=0;
		REP(loop, K+1) {
			deque<int> nq;
			VI vis(2*K+1);
			while(q.size()) {
				int idx = q.front();q.pop_front();
				//cout<<"loop "<<loop<<" pop "<<idx<<endl;
				if(loop==K && Same[idx+K]) {
					//cout<<" same at Kth"<<endl;
					OK=1;break;
				}
				if(loop<K && !Same[idx+K]) {
					//cout<<" not same"<<endl;
					if(!vis[idx-1+K]) {vis[idx-1+K]=1; nq.PB(idx-1); }
					if(!vis[idx+1+K]) {vis[idx+1+K]=1; nq.PB(idx+1); }
				}
			}
			//cout<<"NQ "<<nq<<endl;
			q = nq;
			if(q.size()==0) break;
		}
		
		
		cout<<(OK?"YES":"NO")<<endl;
		//break;
	}
	
	return 0;
}

Codeforces #159 Div1 C - Number Transformation

12:44 |  Codeforces #159 Div1 C - Number Transformation - kojingharangの日記 を含むブックマーク はてなブックマーク -  Codeforces #159 Div1 C - Number Transformation - kojingharangの日記  Codeforces #159 Div1 C - Number Transformation - kojingharangの日記 のブックマークコメント

  • a = a-1 か a = a-(a % x) (x in [2, k]) の操作ができる。
  • a==b になるまでの最小の操作回数を求める問題。
  • 1≦b≦a≦10**18, 2≦k≦15
  • 2から15までの最小公倍数が 360,360 なのでこの単位でなんかできるんじゃないかというところから進まず。
|