SRM 511 DIV1 Medium FiveHundredEleven

http://apps.topcoder.com/stat?c=problem_statement&pm=11484&rd=14536

問題

0〜511の数値が書かれたカードが何枚か(1<=N<=50)ある。
値を記録できるメモリがあって、最初は0である。
プレイヤー2人がいて、交互にカードの中から一枚選び、そのカードの数値とメモリの値を bitwise-OR する。そのカードはゲームから取り除かれ、もう使えない。
このとき、そのターンでメモリの値が511になってはいけない。
カードが何も選べなくなった場合そのプレイヤーの負け。
両者が最善をつくすとき、勝つプレイヤーはどちらか?

回答

まず、メモリの値は512通りしか無いのでそのまま状態として持つことが出来るが、カードが使われているかをそのまま持つと2^50となり、それは出来ない。
この時、((mem|card)!=mem)となる場合(もしくは、ビット集合に含まれていない、という意味で(mem&card)!=card)、これは使われてないことがわかることに注意する。
ただ、((mem|card)==mem)となる場合はどうすれば良いか?
そのカードを使ってもメモリの内容は変わらないことに注意すると、そういうカードが複数枚あっても同一視することができる。
また、カードを使った時そのカードは以降の状態で((mem|card)==mem)となるのだから、((mem|card)==mem)となるカードは、それが((mem|card)!=mem)なら少なくとも1つ増え、((mem|card)==mem)だったらそのカード分の1つ減るだけ、ということを考える。
つまり、使ったカードの枚数を状態として数えておき、((mem|card)==mem)である枚数がそれより大きい場合、使われてないカードがあるということだから、ターンを回す事ができる、というふうにできる。

コメント

Editorial見た。
何か戦略があるのかな、とか考えちゃったけど、なるほど、一見独立に見える(見えないかもしれないが)状態を融合し、圧縮できるということか。考え方として重要だ。

コード

#include <string>
#include <vector>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
#include <iostream>
#include <sstream>
#include <cstdio>
#include <cmath>
#include <ctime>
#include <cstring>
#define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i))
#define rer(i,l,u) for(int (i)=(int)(l);(i)<=(int)(u);++(i))
#define reu(i,l,u) for(int (i)=(int)(l);(i)<(int)(u);++(i))
#define aut(v, x) __typeof(x) v = (x)
#define each(it,o) for(aut(it, (o).begin()); it != (o).end(); ++ it)
#define all(o) (o).begin(), (o).end()
#define pb(x) push_back(x)
#define mp(x,y) make_pair((x),(y))
#define mset(m,v) memset(m,v,sizeof(m))
using namespace std;
typedef long long ll; typedef vector<int> vi; typedef pair<int,int> pii; typedef vector<pii> vpii;
const int INF = 0x3f3f3f3f; const ll INFL = 0x3f3f3f3f3f3f3f3fLL;

bool m[512][52]; bool v[512][52];
struct FiveHundredEleven {
	vi c; int n;
	bool f(int x,int p) {
		if(v[x][p])return m[x][p];
		if(x==511)return true;
		bool r = false;
		int t = 0;
		each(i,c)if((x|*i)==x)++t;
		r|=t>p && !f(x,p+1);
		each(i,c)if((x|*i)!=x){
			r |= !f(x|*i,p+1);
		}
		v[x][p]=1;
		return m[x][p]=r;
	}
	string theWinner(vector <int> cards) {
		c = cards; n = c.size();
		mset(v,0);
		return f(0,0)?"Fox Ciel":"Toastman";
	}
};