Hatena::Grouptopcoder

kojingharangの日記 RSSフィード

|

2013-05-18

SRM 579 Div1 250 UndoHistory

04:35 |  SRM 579 Div1 250 UndoHistory - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 579 Div1 250 UndoHistory - kojingharangの日記  SRM 579 Div1 250 UndoHistory - kojingharangの日記 のブックマークコメント

  • へんなエディタで目的の文字列[]を打つための操作回数の最小値を求める問題。
  • 編集エリアでは, 文字を打つ(1key), 履歴から呼び出す(2click), enter(1key)で内容を行として結果エリアに追記, のどれかができる。
  • 編集エリアに現れた文字列はすべて履歴に追記される
  • 目的の文字列の行数≦50, 各行の文字数≦50
  • enter後2回目以降に履歴を呼び出すと編集エリアに追記されるのだな(誤読)
  • 提出→再提出→チャレンジされず→failed system test
  • (あとで)
  • なんで以前の prefix すべて履歴にあるとしていいのか不思議だったけど編集エリアには履歴から追記できないのでそうなのであった。
  • 最近誤読が多いので落ち着くべき
  • (accepted in practice room)
class UndoHistory {
	public:
	int minPresses(vector <string> L) {
		int ans = 0;
		REP(i, L.size()) {
			int lans = 1<<30;
			if(i>0 && L[i].find(L[i-1])==0) {
				// 前のをそのまま使う + 残りを打つ
				lans = L[i].size() - L[i-1].size();
			}
			// 必要ならクリアしたあとに全部打つ
			lans = min<int>(lans, (i==0 ? 0 : 2) + L[i].size());
			RANGE(j, 0, i) REP(k, L[j].size()+1) if(L[i].find(L[j].substr(0, k))==0) {
				// 履歴から呼び出したあとに全部打つ
				lans = min<int>(lans, 2 + L[i].size() - k);
			}
			ans += lans + 1;
		}
		return ans;
	}
};

2013-05-12

2013 TCO Round2C 550 TheMountain

17:06 |  2013 TCO Round2C 550 TheMountain - kojingharangの日記 を含むブックマーク はてなブックマーク -  2013 TCO Round2C 550 TheMountain - kojingharangの日記  2013 TCO Round2C 550 TheMountain - kojingharangの日記 のブックマークコメント

  • WxH の表 g の一部が与えられる。0≦tx<W, 0≦ty<H のどこかに頂上(tx, ty) を決めてそこが頂上になるよう他のマスを決めたい。
  • (0≦x<tx なら g[y][x]<g[y][x+1], tx≦x<W なら g[y][x]>g[y][x+1])
  • (0≦y<ty なら g[y][x]<g[y+1][x], ty≦y<H なら g[y][x]>g[y+1][x])
  • どこかが頂上になるような時の表の数字の合計値の最小値を求める問題。
  • W, H≦200, 与えられる数字≦1000
  • 250 で Warshall-Floyd を書いて満足してしまった dist-1 勢だったので時間がある
  • が、Naive解を書いた所で終了
  • (あとで)
  • 頂上の場所によらず、頂上より左上にあるエリアは左上から単調増加になるように決めていけばいいので最初に1回だけ累積和を求めておく
  • 右上、左下、右下も同じ。
  • 頂上の場所の候補それぞれについて
  • 尾根のところ(tx==x || ty==y)の数値を決める(左右/上下から上がってきたやつの最大値)
  • 尾根の合計とそれ以外の左上、右上、左下、右下部分の累積和を合計
  • 本番でミスなしで書ける気がしなし
  • (accepted in practice room)
#define INF (1LL<<30)

class TheMountain {
	public:
	int minSum(int H, int W, vector <int> IY, vector <int> IX, vector <int> IE) {
		int ans=INF;
		VVI init(H+2, VI(W+2, 0));
		VVI w[4];
		REP(i, 4) w[i] = init;
		REP(i, IY.size()) init[IY[i]+1][IX[i]+1] = IE[i];
		VVI dp[4];
		REP(i, 4) dp[i] = VVI(H+2, VI(W+2, 0));
		int sx[] = {0, W-1, 0, W-1};
		int sy[] = {0, 0, H-1, H-1};
		int dx[] = {1, -1, 1, -1};
		int dy[] = {1, 1, -1, -1};
		REP(dir, 4) {
			int Dx = dx[dir];
			int Dy = dy[dir];
			for(int y=sy[dir]; 0<=y && y<H; y+=Dy) for(int x=sx[dir]; 0<=x && x<W; x+=Dx) {
				w[dir][y +1][x +1] = -1;
				int v = max(init[y+1][x+1], max(w[dir][y +1][x-Dx +1], w[dir][y-Dy +1][x +1])+1);
				if((w[dir][y +1][x-Dx +1]!=-1 && w[dir][y-Dy +1][x +1]!=-1) && (init[y+1][x+1]==0 || init[y+1][x+1] == v)) {
					w[dir][y +1][x +1] = v;
				}
				
				dp[dir][y +1][x +1] = -1;
				int py = dp[dir][y-Dy +1][x +1];
				int px = dp[dir][y +1][x-Dx +1];
				int pxy = dp[dir][y-Dy +1][x-Dx +1];
				if(!(w[dir][y +1][x +1]==-1 || px==-1 || py==-1 || pxy==-1)) {
					dp[dir][y +1][x +1] = w[dir][y +1][x +1] + py + px - pxy;
				}
			}
		}
		REP(ty, H) REP(tx, W) {
			VI wx(W), wy(H);
			int ok=1;
			RANGE(x, 0, tx+1) { int a=w[0][ty+1][x+1], b=w[2][ty+1][x+1]; wx[x]=max(wx[x], max(a, b)); if(min(a, b)==-1) ok=0; }
			RANGE(x, tx, W)   { int a=w[1][ty+1][x+1], b=w[3][ty+1][x+1]; wx[x]=max(wx[x], max(a, b)); if(min(a, b)==-1) ok=0; }
			RANGE(y, 0, ty+1) { int a=w[0][y+1][tx+1], b=w[1][y+1][tx+1]; wy[y]=max(wy[y], max(a, b)); if(min(a, b)==-1) ok=0; }
			RANGE(y, ty, H)   { int a=w[2][y+1][tx+1], b=w[3][y+1][tx+1]; wy[y]=max(wy[y], max(a, b)); if(min(a, b)==-1) ok=0; }
			if(!ok) continue;
			
			int wx_sum=0, wy_sum=0;
			REP(x, W) wx_sum += wx[x];
			REP(y, H) wy_sum += wy[y];
			int wxy = wx_sum + wy_sum - min(wx[tx], wy[ty]);
			
			int v0 = dp[0][ty-1 +1][tx-1 +1];
			int v1 = dp[1][ty-1 +1][tx+1 +1];
			int v2 = dp[2][ty+1 +1][tx-1 +1];
			int v3 = dp[3][ty+1 +1][tx+1 +1];
			if(v0==-1 || v1==-1 || v2==-1 || v3==-1) continue;
			int v4 = v0+v1+v2+v3;
			int lans = wxy + v4;
			ans = min(ans, lans);
		}
		return ans==INF ? -1 : ans;
	}
};

2013-05-03

SRM 578 Div1 250 GooseInZooDivOne (追記あり)

12:19 |  SRM 578 Div1 250 GooseInZooDivOne (追記あり) - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 578 Div1 250 GooseInZooDivOne (追記あり) - kojingharangの日記  SRM 578 Div1 250 GooseInZooDivOne (追記あり) - kojingharangの日記 のブックマークコメント

  • W*H grid の各セルに鳥がいるかいないかが与えられる。
  • ガチョウは2羽以上の偶数羽いて、ガチョウからマンハッタン距離 D 以内の鳥はガチョウである。
  • 可能なガチョウの配置の組み合わせ数を mod 1,000,000,007 で求める問題。
  • 1≦W, H≦50
  • 1≦D≦100
  • あるセルがガチョウだとした場合にガチョウと確定する鳥たちを同値類としてその構成人数を数えていく
  • Even = 偶数羽のグループ個数, Odd = 奇数羽のグループ個数として
  • Even は独立に採用/不採用できて, Odd は偶数個だけ一度に採用できるので
  • ( 2^Even * ΣCombination(Odd, i) (for i in Odd 以内の偶数) ) - 1 (0個の分) が答え。
  • 1,000,000,007 は素数なので逆元のやつを使用
  • 最後のサンプルが 40 分ほどずっと合わなくておわり
  • (あとで)
  • Each bird within Manhattan distance dist of any goose is also a goose.
  • ぴったりじゃなくて "within" でした\(^o^)/
  • Accepted in practice room
#define MAXN 10010
ll facts[MAXN];
ll inv_facts[MAXN];

static const ll MODVAL = 1000000007;
ll MODADD(ll x, ll y) { return (x+y)%MODVAL; }
ll MODSUB(ll x, ll y) { return (x-y+MODVAL)%MODVAL; }
ll MODMUL(ll x, ll y) { return (x*y)%MODVAL; }
ll MODPOW(ll x, ll e) { ll v = 1; for(;e;x=MODMUL(x,x),e>>=1) if(e&1) v = MODMUL(v, x); return v; }
ll MODINV(ll x) { return MODPOW(x, MODVAL-2); }
ll MODCOMB(ll n, ll r) {
	assert(0<=n && n<MAXN);
	assert(0<=r && r<=n);
	ll vv = MODMUL(facts[n], MODMUL(inv_facts[r], inv_facts[n-r]));
	return vv;
}

void gen_facts() {
	facts[0] = 1;
	inv_facts[0] = MODINV(facts[0]);
	RANGE(i, 1, MAXN) facts[i] = MODMUL(facts[i-1], i);
	RANGE(i, 1, MAXN) inv_facts[i] = MODMUL(inv_facts[i-1], MODINV(i));
	
	REP(i, MAXN) assert(MODMUL(facts[i], inv_facts[i])==1);
}

int co=0;
int W, H;
void f(vector<string>& F, int D, int x, int y) {
	if(F[y][x]!='v') return;
	co++;
	F[y][x]='x';
	
	REP(yy, H) REP(xx, W) {
		if(abs(yy-y)+abs(xx-x) <= D) {
			if(0<=xx && xx<W && 0<=yy && yy<H && F[yy][xx]=='v') {
				f(F, D, xx, yy);
			}
		}
	}
}

class GooseInZooDivOne {
	public:
	int count(vector <string> F, int D) {
		gen_facts();
		H=F.size();
		W=F[0].size();
		int even=0, odd=0;
		int sum=0;
		int sumCo=0;
		REP(y, H) REP(x, W) if(F[y][x]=='v') sum++;
		REP(y, H) {
			REP(x, W) {
				if(F[y][x]=='v') {
					co=0;
					f(F, D, x, y);
					if(co&1) odd++; else even++;
					//cout<<"co "<<co<<endl;
					//cout<<F<<endl;
					sumCo+=co;
				}
			}
		}
		assert(sum==sumCo);
		cout<<even<<" "<<odd<<endl;
		ll ea = 1;
		REP(i, even) {
			ea = (ea * 2) % MODVAL;
		}
		ll oa = 0;
		for(int i=0;i<=odd;i+=2) {
			oa = (oa + MODCOMB(odd, i)) % MODVAL;
		}
		cout<<ea<<" "<<oa<<endl;
		return (((ea * oa) % MODVAL) - 1 + MODVAL) % MODVAL;
	}
};

  • (追記)
  • ΣnCi (iは偶数) == 2^(n-1) を使うとちょっと簡潔になりました
  • (accepted in practice room)
static const ll MODVAL = 1000000007;

int co;
int W, H;
void f(vector<string>& F, int D, int x, int y) {
	if(F[y][x]!='v') return;
	F[y][x]='x';
	co++;
	
	REP(yy, H) REP(xx, W) {
		if(abs(yy-y)+abs(xx-x) <= D) {
			if(0<=xx && xx<W && 0<=yy && yy<H && F[yy][xx]=='v') {
				f(F, D, xx, yy);
			}
		}
	}
}

class GooseInZooDivOne {
	public:
	int count(vector <string> F, int D) {
		H=F.size();
		W=F[0].size();
		int even=0, odd=0;
		REP(y, H) REP(x, W) {
			if(F[y][x]=='v') {
				co=0;
				f(F, D, x, y);
				if(co&1) odd++; else even++;
			}
		}
		ll ans = 1;
		REP(i, even + max(0, odd-1)) ans = (ans * 2) % MODVAL;
		return (ans - 1 + MODVAL) % MODVAL;
	}
};

2013-04-26

SRM 577 Div1 250 EllysRoomAssignmentsDiv1

02:59 |  SRM 577 Div1 250 EllysRoomAssignmentsDiv1 - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 577 Div1 250 EllysRoomAssignmentsDiv1 - kojingharangの日記  SRM 577 Div1 250 EllysRoomAssignmentsDiv1 - kojingharangの日記 のブックマークコメント

  • rating を持つ N 人でコンテストをやる。それぞれどこかの部屋に割り当てられる。部屋数 R = (N+19)/20
  • rating の降順にソートした後 R 人ずつ部屋が重複しないように割り当てていく。
  • ソート前の最初の人の部屋の rating 平均値の期待値を求める問題。
  • 8≦N≦500
  • 1200≦rating[i]≦3923
  • 期待値うげーー
  • 線形性うんたらでどれがどれだけ寄与するか足していけば良いのだろう
  • N が R で割り切れない場合ややこしそう
  • 部屋の人数が N/R の場合と N/R+1 の場合で各 rating がどれだけ寄与するか式とか図で考える
  • 期待値とはみたいな
  • きりが良いときは ( (?+?+?)/?人 + (?+?+?)/?人 + ...全部で[R^部屋の人数]項... ) / R^部屋の人数
  • = Σrating / R / 人数 みたいな
  • きりが良くない場合も考えると
  • (最初の人の)部屋の人数が N/R 人になる確率、そうでない確率が分かるので、人数別の期待値にそれぞれ掛け合わせる感じ
  • R 個ずつグループ化して、最初の人が含まれるときはその rating, それ以外は rating[i]/グループの人数 が寄与する
  • ほとんどのサンプルがちょっとずつ合わない
  • ソートするの見落としてた
  • 最初の人がソートした後何番目にくるかとか考えないといけないじゃんよ
  • ほとんどのサンプルがちょっとずつ合わない
  • おわり
  • (あとで)
  • 最初の人がソートした後最後のはしたグループに来る時だけ特別っぽくて、そのときは部屋の人数は N/R+1 人と確定してるので N/R 人の期待値は足さない。
  • それ以外は上記の考え方でよさげ
  • 変数名がアレゲ
  • 最近 easy ができません
  • Accepted in practice room
class EllysRoomAssignmentsDiv1 {
	public:
	double getAverage(vector <string> RS) {
		string s;
		FOR(e, RS) s+=*e;
		VI w;
		stringstream ss(s);
		int v;
		while(ss>>v) w.PB(v);
		int me = w[0];
		sort(ALLR(w));
		//cout<<w<<endl;
		int N = w.size();
		int ORE=0;
		REP(i, N) if(me==w[i]) ORE=i;
		int R = (N+19)/20;
		cout<<"R "<<R<<endl;
		cout<<"me "<<me<<endl;
		cout<<"ore "<<ORE<<endl;
		double ans = 0;
		double ext = 0;
		int p1 = N-N/R*R;
		double ooi = (double)p1 / R;
		cout<<"p1 "<<p1<<" "<<N/R<<" "<<endl;
		for(int i=0;i<N;i+=R) {
			int NUM = min(R, N-i);
			if(i/R==ORE/R) {
				ans += me;
				continue;
			}
			if(NUM==R) {
				REP(j, NUM) {
					ans += (double)w[i+j] / NUM;
				}
			} else {
				REP(j, NUM) {
					ext += (double)w[i+j] / NUM;
				}
			}
		}
		if((N-1)/R==ORE/R && p1) {
			return (ans+ext) / (N/R+1);
		} else {
			return (1.0-ooi) * ans / (N/R)  +  ooi * (ans+ext) / (N/R+1);
		}
	}
};

2013-03-26

SRM 574 Div1 275 TheNumberGame

00:54 |  SRM 574 Div1 275 TheNumberGame - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 574 Div1 275 TheNumberGame - kojingharangの日記  SRM 574 Div1 275 TheNumberGame - kojingharangの日記 のブックマークコメント

  • a, b が整数 A, B を持っている。1ターンで自分の数字の順番を逆にするか下1桁を消すことができる。1000回以内にA=Bになれば a の勝ち、そうでなければ負け。
  • 2人が最適に行動したとき a が勝つかどうかを求める。
  • 1≦A, B≦999,999,999, A!=B, A,B には10進表記で 0 は含まれない
  • 数字はせいぜい 9 桁なので、1000回もあれば勝敗はついていると思われる。→この条件は忘れる
  • b は数字を消すと負ける可能性が高まるはず. せっかく 789 だったのに 78 にするなんて!
  • なので、b はひたすら B をひっくり返していればいい
  • A から作れる数字をすべてリストアップして、それに B が含まれれば a が勝つ。
  • Accepted
  • つまり A に B か B の逆が含まれればよいのか... と思ってみるとこの実装はやぼったい。
class TheNumberGame {
	public:
	string determineOutcome(int A, int B) {
		vector<string> w;     // なにこれ
		stringstream ss, sb;
		sb<<B;
		ss<<A;
		string s=ss.str();
		REP(i, 2) {
			if(i==1) reverse(ALL(s));
			REP(sj, s.size()) {
				RANGE(ej, sj, s.size()) {
//					cout<<s.substr(sj, ej-sj+1)<<endl;
					if(s.substr(sj, ej-sj+1)==sb.str()) return "Manao wins";
				}
			}
		}
		return "Manao loses";
	}
};

SRM 574 Div1 450 PolygonTraversal

14:07 |  SRM 574 Div1 450 PolygonTraversal - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 574 Div1 450 PolygonTraversal - kojingharangの日記  SRM 574 Div1 450 PolygonTraversal - kojingharangの日記 のブックマークコメント

  • 円上に時計回りに N 個の点があって、点番号 P[0]→P[1]→...→P[M-1] と線が引いてある。
  • P[M-1] から使ってない点をすべて使って P[0] に戻りたい。その際、新しく引く線はかならずどれかの線と交差しないといけない。
  • そんな線の引き方は何通りあるか。
  • 4≦N≦18, 2≦M≦N-1, 1≦P[i]≦N, P is distinct
  • ほげ〜
  • 2**18 通り何かしらやるのだろう
  • 使った頂点を状態とすると 2**18 になる
  • dp[使用済みノード集合] ... その状態での組み合わせ数?
  • 交差してるか決めるには、とりあえず引こうとする線の始点と終点がないといけない
  • dp[最後に使ったノード][使用済みノード集合] ... その状態での組み合わせ数  としてみる
  • 交差は分からないけどなんとなく線の両側にノードが1コ以上ずつあればいい気がする
  • 初期状態は P の線分を全部引いた状態が 1 通りあるので dp[Pの最後][Pに含まれるノード集合] = 1
  • 更新式は、cur, next を決めて cur が使われてて next が使われてなくて交差するなら dp[next][bit|1<<next] += dp[cur][bit]
  • 書いたけど合わない
  • (あとで)
  • Petr さんのを観察
  • 骨格とか交差判定は良かったみたいだが、なんかそもそも更新の方向とか計算順序が逆。
  • あと全ノードを使ったあとに最初のノードに戻ってくる所の特殊処理が抜けてた。
  • Fastest の _Romka_ さんは 9 分 11 秒だそうです。ほげ〜〜〜
  • ↓あとで(accepted in practice room)
// maximum 476ms in arena
int cross(int N, int bit, int cur, int nxt) {
	// cur, nxt を結んだ線の両サイドに少なくとも1コ以上使用済みノードがあれば交差する
	int side0 = 0;
	for(int i=(cur+1)%N; i!=nxt; i=(i+1)%N) if(bit & 1<<i) side0=1;
	int side1 = 0;
	for(int i=(nxt+1)%N; i!=cur; i=(i+1)%N) if(bit & 1<<i) side1=1;
	return side0 && side1;
}

class PolygonTraversal {
	public:
	long long count(int N, vector <int> P) {
		int M = P.size();
		REP(i, M) P[i]--;
		
		VVI dp(N, VI(1<<N));
		int init_bit = 0;
		FOR(e, P) init_bit |= 1<<*e;
		dp[P.back()][init_bit] = 1;
		
		REP(bit, 1<<N) {
			REP(cur, N) {
				if((bit & 1<<cur)==0) continue;
				if(dp[cur][bit]==0) continue;
				REP(nxt, N) {
					if(bit & 1<<nxt) continue;
					int ok = cross(N, bit, cur, nxt);
					if(ok) dp[nxt][bit|1<<nxt] += dp[cur][bit];
				}
			}
		}
		ll ans = 0;
		int bit = (1<<N)-1;
		REP(cur, N) {
			int nxt = P[0];
			int ok = cross(N, bit, cur, nxt);
			if(ok) ans += dp[cur][bit];
		}
		return ans;
	}
};


  • ちなみに 交差判定にビット演算を使った版も。時間はそんなに違わないような。
// maximum 117ms in arena
int crossB(int N, int bit, int cur, int nxt) {
	int a = min(cur, nxt);
	int b = max(cur, nxt);
	int mask0 = ((1<<b)-1) & ~((1<<(a+1))-1);   // 0 0 0 b 1 1 1 a 0 0 0
	int mask1 = ~((1<<(b+1))-1) |  ((1<<a)-1);  // 1 1 1 b 0 0 0 a 1 1 1
	
	int ret = bit & mask0 && bit & mask1;
	return ret;
}

|