Hatena::Grouptopcoder

kojingharangの日記 RSSフィード

 | 

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

 |