Hatena::Grouptopcoder

kojingharangの日記 RSSフィード

|

2013-02-16

Facebook Hacker Cup Round2

19:15 |  Facebook Hacker Cup Round2 - kojingharangの日記 を含むブックマーク はてなブックマーク -  Facebook Hacker Cup Round2 - kojingharangの日記  Facebook Hacker Cup Round2 - kojingharangの日記 のブックマークコメント

起きれたけど o-- (rank 353/407) で Round3 進出ならず。

Round3 勢がんばってください

Facebook Hacker Cup Round2 A - Cake Cutting

19:15 |  Facebook Hacker Cup Round2 A - Cake Cutting - kojingharangの日記 を含むブックマーク はてなブックマーク -  Facebook Hacker Cup Round2 A - Cake Cutting - kojingharangの日記  Facebook Hacker Cup Round2 A - Cake Cutting - kojingharangの日記 のブックマークコメント

  • 直ちに影響がある眠さ(朝6時)
  • 円卓に座ったビジネスマンが握手するやつに似てるので何らかのDPかな
  • ...
  • 1時間くらい
  • 各カットについて、
  • (1) そのカット自身だけで生じる部分 ... ((A[i]-1)*A[i]/2)
  • (2) すでにカットした線分の数に依存する部分 ... すでにカットした線分の数 * (A[i]+1) +1
  • の領域が新たに発生することにしたらサンプルが通った。証明できないしもしもっと複雑な事情であっても分からないだろうからこれでサブミット
  • Accepted (おー)
int main() {
	int T;
	cin>>T;
	REP(ttt, T) {
		ll N;
		cin>>N;
		VI A(N);
		REP(i, N) cin>>A[i];
		
		ll lines=0;
		ll ans=1;
		
		REP(i, N) {
			ans += lines*(A[i]+1) + ((A[i]-1)*A[i]/2) +1;
			lines += A[i]+1;
		}
		cout<<"Case #"<<ttt+1<<": "<<ans<<endl;
//		break;
	}
	return 0;
}



Facebook Hacker Cup Round2 B - RoboElection

23:25 |  Facebook Hacker Cup Round2 B - RoboElection - kojingharangの日記 を含むブックマーク はてなブックマーク -  Facebook Hacker Cup Round2 B - RoboElection - kojingharangの日記  Facebook Hacker Cup Round2 B - RoboElection - kojingharangの日記 のブックマークコメント

  • N台のロボットがいて何回か投票することになった。投票率がP%以上になるまで先頭のK台が消されていく。ロボットはlazyなので自分が消される可能性があるときだけ投票する。各ロボットが最適に動いた場合に何回の投票が行われるかを求める問題。
  • 0<K≦N≦10**12, 0<P≦100
  • よくわからんので遅いけど正しい naive 解を作ることにする
  • K台ずつ消されてくので、K台ごとに運命共同体グループに分ける。最後のグループから順に、どう行動するか決めてく。
  • 自分らが消されるかどうかが問われる投票にて、自分らの投票数と自分より後のグループの(やむを得ない)投票数が条件を満たすなら当該グループは生き残れる。
  • 生き残れるグループはそれより前のグループが消されるかどうか問われる投票では投票しない
  • サンプル通る
  • で、デバッグ出力とか見てると、投票率がP以上になる→後ろのやつらが投票しなくなるので投票率がガクッと下がる→徐々に上がっていく→...を繰り返すようなので
  • 徐々に上がっていく部分を二分探索で高速化してみる
  • 仕込んでデバッグしてるうちに終了
  • やっぱ証明とか数式立てるとかしてもうちょっとスマートにやりたいもんだなぁ
  • ↓あとで 正解者の答えと一致した版 (141s)
ll N, K, P;

int main() {
	int T;
	cin>>T;
	REP(ttt, T) {
		cin>>N>>K>>P;
		ll nz = 0;
		ll ans = 1;
		if(P==100) {
			ans = (N+K-1)/K;
		} else {
			for(ll all=N%K==0 ? K : N%K;all<=N;all+=K) {
				ll n = min(K, all);
				ll vote=n + nz;
				if(vote * 100 >= all * P) {
					nz=0;
					ans = 1;
				} else {
					if(n==K && (N-all)/K > 10) {
						ll lo=0,hi=(N-all)/K;
						int hiv;
						{
							ll n_vote = K + nz + K*hi;
							ll n_all = all + K*hi;
							hiv = (n_vote * 100 >= n_all * P);
						}
						if(hiv) {
							while(lo+1<hi) {
								ll mid = (lo+hi)/2;
								ll n_vote = K + nz + K*mid;
								ll n_all = all + K*mid;
								//cout<<"BS "<<mid<<" "<<(float)n_vote/n_all<<" "<<n_vote<<" "<<n_all<<endl;
								if(n_vote * 100 >= n_all * P) hi=mid; else lo=mid;
							}
							//cout<<"BSS hi="<<hi<<" init hi:"<<(N-all)/K<<" hiv:"<<hiv<<endl;
							ans += hi;
							nz += K*hi;
							all += K*hi - K;
						} else {
							ans++;
							nz += n;
						}
					} else {
						ans++;
						nz += n;
					}
				}
				//all += n;
				//cout<<" "<<n<<" "<<all<<" vote "<<vote<<" r "<<(float)vote/all<<" -> "<<(vote * 100 >= all * P)<<endl;
			}
		}
		cout<<"Case #"<<ttt+1<<": "<<ans<<endl;
//		break;
	}
	return 0;
}

2013-02-12

AtCoder Regular Contest #012 C. 五目並べチェッカー

22:18 |  AtCoder Regular Contest #012 C. 五目並べチェッカー - kojingharangの日記 を含むブックマーク はてなブックマーク -  AtCoder Regular Contest #012 C. 五目並べチェッカー - kojingharangの日記  AtCoder Regular Contest #012 C. 五目並べチェッカー - kojingharangの日記 のブックマークコメント

  • 19x19の盤で五目並べの状態が与えられる。o が先手。x が後手。途中または勝負終了の状態としてあり得るかどうかを返す問題。
  • 0≦ oの数 - xの数 ≦1をチェック
  • ある色が勝ってるか判定するルーチンを用意して、
  • どっちも勝ってるといったことがないかチェック
  • どっちかが勝ってたらさらに ox の数に制約が生ずるのでそれもチェック
  • 勝負がついていたら、確かに最後の手で勝負がついたかどうかをチェック。
  • これには、勝った色のマスのどれかについて、「そのマスを取り消したら勝負がついてない状態である」ようなマスがあるならOK。
  • そうでないとき(どう1手戻しても勝ってるとき)はだめ
  • 感想→計算量を落とす問題もいいけど、こういう書くのが面倒そうなやつを簡潔に早く書くみたいな問題も面白かった。
  • ちょっとした日常のコードを書くための筋トレによさげ。
  • accepted

int win(char col, vector<string>& w) {
	int dx[]={1,1,0,-1,-1,-1,0,1};
	int dy[]={0,1,1,1,0,-1,-1,-1};
	REP(y, 19) REP(x, 19) {
		REP(dir, 8) {
			int win=1;
			REP(st, 5) {
				int nx=x+dx[dir]*st;
				int ny=y+dy[dir]*st;
				if(!(0<=nx&&nx<19&&0<=ny&&ny<19)) {win=0;break;}
				if(w[ny][nx]!=col) {win=0;break;}
			}
			if(win) return 1;
		}
	}
	return 0;
}

int solve(vector<string>& w) {
	int Bs=0, Ws=0;
	REP(i, 19) REP(j, 19) Bs+=w[i][j]=='o';
	REP(i, 19) REP(j, 19) Ws+=w[i][j]=='x';
	
	//cout<<"count: "<<Bs<<" "<<Ws<<endl;
	if(!(Bs==Ws || Bs==Ws+1)) return 0;
	
	string col("ox");
	int Bwin = win('o', w);
	int Wwin = win('x', w);
	//cout<<"win: "<<Bwin<<" "<<Wwin<<endl;
	
	if(Bwin && Wwin) return 0;
	if(!(!Bwin || (Bwin && Bs==Ws+1))) return 0;
	if(!(!Wwin || (Wwin && Bs==Ws))) return 0;
	if(Bwin) {
		int ok=0;
		REP(y, 19) REP(x, 19) {
			if(ok) break;
			if(w[y][x]=='o') {
				w[y][x]='.';
				if(!win('o', w)) {ok=1;break;}
				w[y][x]='o';
			}
		}
		if(!ok) return 0;
	}
	if(Wwin) {
		int ok=0;
		REP(y, 19) REP(x, 19) {
			if(ok) break;
			if(w[y][x]=='x') {
				w[y][x]='.';
				if(!win('x', w)) {ok=1;break;}
				w[y][x]='x';
			}
		}
		if(!ok) return 0;
	}
	return 1;
}

int main() {
	//ios::sync_with_stdio(false);
	string s;
	while(getline(cin, s)) {
		vector<string> w(19);
		w[0] = s;
		RANGE(i, 1, 19) getline(cin, w[i]);
		//cout<<w<<endl;
		int ok = solve(w);
		
		
		cout<<(ok?"YES":"NO")<<endl;
		//break;
	}
	
	return 0;
}

2013-02-04

Facebook Hacker Cup Round1

19:32 |  Facebook Hacker Cup Round1 - kojingharangの日記 を含むブックマーク はてなブックマーク -  Facebook Hacker Cup Round1 - kojingharangの日記  Facebook Hacker Cup Round1 - kojingharangの日記 のブックマークコメント




Failed Wake Up Test




(...のはずが 384 位で通過しました... が、だめだと思って事前に作ってあったので残しときます:)

Facebook Hacker Cup Round1 A - Card Game

19:32 |  Facebook Hacker Cup Round1 A - Card Game - kojingharangの日記 を含むブックマーク はてなブックマーク -  Facebook Hacker Cup Round1 A - Card Game - kojingharangの日記  Facebook Hacker Cup Round1 A - Card Game - kojingharangの日記 のブックマークコメント

  • N個の数が与えられる。そこからK個選んで、その中で最大の数を得点とする。K個取るすべての組み合わせについて、得点の合計を mod 1000000007 で求める問題。
  • 1≦K≦N, N≦10000, 0≦a[i]≦2*10^9
  • 最大の数を決めると、それが最大になるような選び方は Combination(それより小さい数の個数, K-1) 通りある。ので、全ての最大の数についてその数 * Combination(...) を足したものが答え。
  • 「それより小さい数の個数」は最初に1回ソートすればわかる
  • Combination の分母の部分は 1000000007 は素数なのでフェルマーの小定理より x * x^(1000000007-2) ≡ 1 (mod 1000000007) であることを使った。
  • しまった ans に足していくところで mod をとってなかった
  • 冷静に出力を見ると明らかに 1000000007 より大きな数が出てるからおかしいと気づくことはできたかもしれないけど、気づかないなぁ。
  • ↓正解者の答えと一致した版

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); }

#define MAXN 10010
ll facts[MAXN];
ll inv_facts[MAXN];

ll MODCOMB(ll n, ll r) {
	assert(0<=n && n<MAXN);
	assert(0<=r && r<=n);
	return MODMUL(facts[n], MODMUL(inv_facts[r], inv_facts[n-r]));
}

int main() {
	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 T;
	cin>>T;
	REP(ttt, T) {
		ll N, K;
		cin>>N>>K;
		VI w(N);
		REP(i, N) cin>>w[i];
		sort(ALL(w));
		ll ans = 0;
		REP(i, N) {
			if(i-(K-1)>=0) {
				//ans += MODMUL(w[i], MODCOMB(i, i-(K-1)));           // だめ
				ans = MODADD(ans, MODMUL(w[i], MODCOMB(i, i-(K-1)))); // OK
			}
		}
		cout<<"Case #"<<ttt+1<<": "<<ans<<endl;
	}
	return 0;
}

Facebook Hacker Cup Round1 B - Security

19:32 |  Facebook Hacker Cup Round1 B - Security - kojingharangの日記 を含むブックマーク はてなブックマーク -  Facebook Hacker Cup Round1 B - Security - kojingharangの日記  Facebook Hacker Cup Round1 B - Security - kojingharangの日記 のブックマークコメント

  • 長さ M のセクション L 個からなる、長さ N の文字列 K, K1, K2 のうち K1, K2 が与えられる。K は abcdef だけを含む。K1 は K の 0 文字以上が ? に置き換わっている。K2 は K のセクションの順序が適当に並び替えられた上で 0 文字以上が ? に置き換わっている。
  • K としてありうる辞書順最小の文字列を求める問題。
  • N≦100, M≦50
  • 辞書順最小ということで、制約を満たす状態を保ったまま先頭から貪欲に決めてく。
  • 制約を満たすかどうかは K1 のセクションと K2 のセクション間の2部マッチングで。といいつつSpaghetti Sourceさんのフローライブラリをぺたぺた。
  • 各文字ペアのどれかが「両方 a〜f かつ文字が違う」というときだけマッチしないので辺を張らない。流量が M なら制約を満たす。
  • Accepted
/////////////////////// [OPTION] maximumFlow
#define INF 1000000
typedef int Weight;
struct Edge {
  int src, dst;
  Weight weight;
  Edge(int src, int dst, Weight weight) :
    src(src), dst(dst), weight(weight) { }
};
bool operator < (const Edge &e, const Edge &f) {
  return e.weight != f.weight ? e.weight > f.weight : // !!INVERSE!!
    e.src != f.src ? e.src < f.src : e.dst < f.dst;
}
typedef vector<Edge> Edges;
typedef vector<Edges> Graph;

typedef vector<Weight> Array;
typedef vector<Array> Matrix;

#define RESIDUE(s,t) (capacity[s][t]-flow[s][t])
Weight maximumFlow(const Graph &g, int s, int t) {
  int n = g.size();
  Matrix flow(n, Array(n)), capacity(n, Array(n));
  REP(u,n) FOR(e,g[u]) capacity[e->src][e->dst] += e->weight;

  Weight total = 0;
  while (1) {
    queue<int> Q; Q.push(s);
    vector<int> prev(n, -1); prev[s] = s;
    while (!Q.empty() && prev[t] < 0) {
      int u = Q.front(); Q.pop();
      FOR(e,g[u]) if (prev[e->dst] < 0 && RESIDUE(u, e->dst) > 0) {
        prev[e->dst] = u;
        Q.push(e->dst);
      }
    }
    if (prev[t] < 0) return total; // prev[x] == -1 <=> t-side
    Weight inc = INF;
    for (int j = t; prev[j] != j; j = prev[j])
      inc = min(inc, RESIDUE(prev[j], j));
    for (int j = t; prev[j] != j; j = prev[j])
      flow[prev[j]][j] += inc, flow[j][prev[j]] -= inc;
    VI path;
    for (int j = t; prev[j] != j; j = prev[j]) path.push_back(j);
    path.push_back(s);
    reverse(ALL(path));
    total += inc;
//    cout<<"Update "<<path<<" -> total "<<total<<endl;
  }
}
void add_edge(Graph& g, int s, int e, int w) {
	g[s].push_back(Edge(s, e, w));
	g[e].push_back(Edge(e, s, 0));
}
/////////////////////// maximumFlow

ll N, M, L;
string K1, K2;

int check() {
	Graph g(M*2+2, Edges());
	int S=M*2, E=M*2+1;
	REP(i, M) {
		add_edge(g, S, i, 1);
		add_edge(g, M+i, E, 1);
	}
	// i in K1, j in K2
	REP(i, M) REP(j, M) {
		int lok=1;
		REP(li, L) {
			char a=K1[i*L+li], b=K2[j*L+li];
			if(!(a=='?' || b=='?') && a!=b) lok=0;
		}
		if(lok) add_edge(g, i, M+j, 1);
	}
	int flow = maximumFlow(g, S, E);
	return flow==M;
}

int main() {
	int T;
	cin>>T;
	REP(ttt, T) {
		cin>>M;
		cin>>K1>>K2;
		N=K1.size();
		L=N/M;
		
		int gok=1;
		REP(ci, N) {
			if(K1[ci]=='?') {
				int ok=0;
				REP(cand, 6) {
					K1[ci]=cand+'a';
					if(check()) {ok=1;break;}
				}
				if(!ok) {gok=0;break;}
			}
		}
		if(!check()) gok=0;
		
		if(gok) {
			cout<<"Case #"<<ttt+1<<": "<<K1<<endl;
		} else {
			cout<<"Case #"<<ttt+1<<": IMPOSSIBLE"<<endl;
		}
	}
	return 0;
}



Facebook Hacker Cup Round1 C - Dead Pixels

19:32 |  Facebook Hacker Cup Round1 C - Dead Pixels - kojingharangの日記 を含むブックマーク はてなブックマーク -  Facebook Hacker Cup Round1 C - Dead Pixels - kojingharangの日記  Facebook Hacker Cup Round1 C - Dead Pixels - kojingharangの日記 のブックマークコメント

  • W * H ピクセルのモニターがあって N 個のピクセル (x[i], y[i]) が壊れている。P * Q の画像が正しく表示できるような位置は何個あるかを計算する問題。
  • x[i], y[i] は次の漸化式で与えられる。x[0]=X, y[0]=Y, x[i]=(x[i-1] * A + y[i-1] * B + 1) % W, y[i]=(x[i-1] * C + y[i-1] * D + 1) % H (for 0 < i < N)
  • 1≦W,H≦40000, 1≦P≦W, 1≦Q≦H, 1≦N≦min(10^6, W*H), 0≦X,Y<W, 1≦A,B,C,D≦100
  • パラメータ多いな
  • 壊れたピクセルの位置に対して、画像が置けないような画像左上の座標セットが決まる。そこが重複する場合をなんとかすればよさそう
  • 大きさ H の配列に、あと何ピクセル右までだめかを保持しておいて、x=0 から x=W+P-1 まで縦の走査線を横にずらしていく感じ。
  • 壊れてるピクセルを x 座標でソートして、現在の走査線から始まるやつについて、だめな y の範囲に対して max(現状のだめ数, 横にあと何ピクセルだめか) で更新する
  • 走査線が変わったら、だめ数を 1 ずつ減らす。
  • だめ数>0 ならそのピクセルはだめ
  • メモリO(H) 時間 O(WH+NH) くらい
  • 最大ケースを 20 個流して -O3 で 4 分くらいで終わることが確認できたので submit
  • Accepted
// X in [x0, x1]
// Y in [y0, y1]
struct Rect {
	int x0, y0, x1, y1;
	bool operator<(const Rect& o) const {
		return x0<o.x0;
	}
};

ostream& operator<<(ostream& os, const Rect& r) {
	os<<"[Rect "<<r.x0<<" "<<r.y0<<" "<<r.x1<<" "<<r.y1<<" ]";
	return os;
}

int W, H, P, Q, N, X, Y, A, B, C, D;

int foo() {
	vector<Rect> rects(N);
	int x=X, y=Y;
	REP(i, N) {
		Rect r = (Rect){x-(P-1), y-(Q-1), x, y};
		r.x0 = max(r.x0, 0);
		r.y0 = max(r.y0, 0);
		r.x1 = min(r.x1, W-1);
		r.y1 = min(r.y1, H-1);
		rects[i] = r;
		int nx = (x * A + y * B + 1) % W;
		int ny = (x * C + y * D + 1) % H;
		x = nx; y = ny;
	}
	sort(ALL(rects));
//	cout<<rects<<endl;
	
	ll ng = 0;
	VI line(H);
	int ri=0;
	REP(scanx, W-P+1) {
		REP(y, H-Q+1) line[y] = max(line[y]-1, 0);
		for(;ri<N && rects[ri].x0==scanx;ri++) {
			int wi = rects[ri].x1 - rects[ri].x0 + 1;
			RANGE(y, rects[ri].y0, rects[ri].y1+1) {
				line[y] = max(line[y], wi);
			}
		}
		REP(y, H-Q+1) ng += line[y] ? 1 : 0;
	}
	int ans = (W-P+1)*(H-Q+1) - ng;
	return ans;
}

int main() {
	int T;
	cin>>T;
	REP(ttt, T) {
		cin>>W>>H>>P>>Q>>N>>X>>Y>>A>>B>>C>>D;
		
		int r1 = foo();
		
		cout<<"Case #"<<ttt+1<<": "<<r1<<endl;
	}
	return 0;
}

2013-02-01

SRM 568 Div1 250 BallsSeparating

08:01 |  SRM 568 Div1 250 BallsSeparating - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 568 Div1 250 BallsSeparating - kojingharangの日記  SRM 568 Div1 250 BallsSeparating - kojingharangの日記 のブックマークコメント

  • N個の箱があってR,G,B色のボールが箱iにそれぞれR[i],G[i],B[i]個入っている。すべての箱について1色のボールだけが含まれるようにするには、ボールをとってどこかに入れる作業を最低何回やる必要があるか返す問題。無理なら -1 を返す。
  • 1≦N≦50, 1≦R[i],G[i],B[i]≦1000000
  • 箱に対して色を決めると、その箱に関するコストが決まる。どこに入れるかはとりあえず考えない
  • 最終的にR,G,Bの箱がそれぞれ1個以上あればよい
  • 色の決め方は、概ねコスト最小の色を選んでいけばよさそうだが、そうすると全く使われない色がある場合になんかきな臭い。
  • DPでやってみる
  • dp[i][bit] ... i番目までの箱について1色しかないようにして、かつ使われた箱の色の組み合わせがbit==R<<2|G<<1|Bである場合の最小コスト
  • 1個前の箱までの色の組み合わせを prev, 今の箱までの色の組み合わせを cur, 今の箱を色 c にするとして
  • (prev|1<<c)==cur になるなら前の箱までのコスト + 今回のコストで今の箱までのやつを更新する</li>
  • 全部使うときの最小コストなので dp[N][7] がこたえ
  • accepted ... だが遅い
#define INF 1<<30

class BallsSeparating {
	public:
	int minOperations(vector <int> R, vector <int> G, vector <int> B) {
		int N=R.size();
		VVI dp(N+1, VI(8, INF));
		dp[0][0]=0;
		
		RANGE(i, 1, N+1) {
			REP(cur, 8) {
				REP(prev, 8) {
					int costs[3];
					int all = R[i-1]+G[i-1]+B[i-1];
					costs[0] = all - R[i-1];
					costs[1] = all - G[i-1];
					costs[2] = all - B[i-1];
					REP(c, 3) {
						if((prev|(1<<c))==cur) {
							dp[i][cur] = min(dp[i][cur], dp[i-1][prev] + costs[c]);
						}
					}
				}
			}
			//cout<<i<<"::: "<<dp<<endl;
		}
		return dp[N][7]==INF ? -1 : dp[N][7];
	}
};

2013-01-31

Facebook hacker cup qualification B. Balanced Smileys

22:35 |  Facebook hacker cup qualification B. Balanced Smileys - kojingharangの日記 を含むブックマーク はてなブックマーク -  Facebook hacker cup qualification B. Balanced Smileys - kojingharangの日記  Facebook hacker cup qualification B. Balanced Smileys - kojingharangの日記 のブックマークコメント

  • [a-z: ()] からなる文字列が与えられる。":)" と ":(" はそれ単体で1文字ともみなせる。カッコの対応が合う解釈があるかどうかを YES NO で答える問題。
  • |文字列|≦100
  • スタックとか使うのか?いやなんかいろいろ反例があって failed system test になりそうなので大変きな臭い。大変きな臭い。
  • 手堅く区間DPでやろう
  • 問題文から以下の定義になる
BalancedParentheses ::=
	  Empty
	| [a-z: ]
	| :)
	| :(
	| ( BalancedParentheses )
	| BalancedParentheses BalancedParentheses
  • ので、始点 S 終点 E で表される長さ L の部分文字列 s が与えられた時、それが BalancedParenthesis かどうかを定義通りに求める。
  • これをすべての S, E に対して行う。ただし L が小さい順にやる。

  • Accepted
int main() {
	int T;
	cin>>T;
	cin.ignore();
	REP(ttt, T) {
		string s;
		getline(cin, s);
		
		int N=s.size();
		VVI dp(N, VI(N+1));
		REP(S, N) dp[S][0]=1;
		
		RANGE(L, 1, N+1) {
			REP(S, N) {
				int E=S+L-1;
				if(E>=N) continue;
				
//				cout<<s.substr(S, L)<<endl;
				
//				cout<<S<<" "<<L<<endl;
				if(L==0) dp[S][L]=1;
				if(L==1 && (s[S]==' ' || s[S]==':' || ('a'<=s[S] && s[S]<='z'))) dp[S][L]=1;
				if(L==2 && (s.substr(S, L)==":)" || s.substr(S, L)==":(")) dp[S][L]=1;
				if(L-2>=0 && s[S]=='(' && dp[S+1][L-2] && s[E]==')') dp[S][L]=1;
				if(L-2>=0) {
					RANGE(l, 1, L) if(dp[S][l] && dp[S+l][L-l]) dp[S][L]=1;
//					RANGE(l, 1, L) assert(0<=S && S<N && S+l<N && 0<L-l && L-l<=N);
				}
			}
		}
		
		cout<<"Case #"<<ttt+1<<": "<<(dp[0][N]?"YES":"NO")<<endl;
//		break;
	}
	return 0;
}

Facebook hacker cup qualification C. Find the Min

22:35 |  Facebook hacker cup qualification C. Find the Min - kojingharangの日記 を含むブックマーク はてなブックマーク -  Facebook hacker cup qualification C. Find the Min - kojingharangの日記  Facebook hacker cup qualification C. Find the Min - kojingharangの日記 のブックマークコメント

  • m[0] = a, m[i] = ( b * m[i-1] + c ) % r (0<i<k), m[i] = m[i-k]からm[i-1]までに含まれない0以上の数(k≦i) のとき、m[n-1] を求める問題。
  • 1≦k≦10^5, k<n≦10^9, 0≦a,b,c≦10^9, 1≦r≦10^9
  • うゲェ
  • 小さいのから埋めてくのでm[k]からm[2k]は0〜kが重複なくふくまれる
  • その後、m[2k+1] を求めるにあたって直前の k 個は 0〜kが重複なく含まれるとこから m[k] が抜けたものなので、そこに含まれないのは m[k] となり、
  • 結局 m[2k+1]==m[k] になるというふうにして以下繰り返すので、m[i] の値としては k+1 周期になる。ある程度進んだあとに (n-1-i)%(k+1)==0なる i で打ち切る。
  • ということで、ある程度m[i]の値を求める部分を工夫して速くすればいい
  • 「m[i-(k-1)]からm[i]までに現れる数字→その個数」という配列を用意して1こ進める度に更新してく
  • 次の数字はO(k)で探す
  • accepted
  • 気づかなかったけど O(k^2) なので実は危なかったのでは

int main() {
	int T;
	cin>>T;
	REP(ttt, T) {
		ll N,K,A,B,C,R;
		cin>>N>>K>>A>>B>>C>>R;
		
		VI w(K);
		w[0] = A;
		RANGE(i, 1, K) {
			w[i] = (B * w[i - 1] + C) % R;
		}
		VI ww(K+2); // [0, K+2)
		REP(j, K) if(w[j]<ww.size()) ww[w[j]]++;
		
		ll ans = -1;
		RANGE(i, K, N) {
			ll nxt = -1;
			REP(j, ww.size()) {
				if(ww[j]==0) {
					nxt = j;
					break;
				}
			}
			assert(nxt!=-1);
			
			int rm = w[i%K];
			if(rm<ww.size()) ww[rm]--;
			
			int add = nxt;
			if(add<ww.size()) ww[add]++;
			
			w[i%K] = nxt;
			
//			if((N-1-i)%(K+1)==0) {
//				cout<<i<<" "<<nxt<<endl;
//			}
			ans = nxt;
			if(5*K<N && (N-1-i)%(K+1)==0) break;
		}
		
		cout<<"Case #"<<ttt+1<<": "<<ans<<endl;
//		break;
	}
	return 0;
}
|