Particle

競技プログラミングについての雑記

AOJ 2376: DisconnectedGame

問題

二人完全情報ゲームをする。
グラフが隣接行列で与えられる。交互に辺を追加していき、グラフを連結にしたほうの負けである。

解法

N(<=1000)で大きいので、全探索では解けない。
とりあえず、Union-Find木を用いて、既に連結になっている部分を調べる。
少し考察すると、(既に連結である部分の頂点数mod 4)を状態として持てば良いことが分かるが、mod 4で状態を持つと状態数が(1000^4)/24程度と多くなりすぎるため、もう少し賢い解法を考える必要がある。

クリークを作るための辺の数の差分の偶奇を考えると実は状態をmod 2で持てば良いことが分かり、状態数も1000^2程度に減る。
mod 2で状態を持った場合は、遷移先が高々2,3個しかしかなく、状態がループすることはない(辺の数は単調増加するので、ゲームが必ず終了する)のでgrundy数で解くことができる。

コード

#include <iostream>
#include <vector>
#include <string>
#include <string.h>
#include <set>
using namespace std;
 
#define N 1000
struct UF {
	int par[N];
	
	void init(int n){
		for(int i = 0; i < n; i++){
			par[i] = i;
		}
	}
	
	int find(int x){
		if(par[x] == x){
			return x;
		}else{
			return par[x] = find(par[x]);
		}
	}
	
	void unite(int x, int y){
		x = find(x);
		y = find(y);
		if(x == y) return;
		par[x] = y;

	}
};
 
int grundy[N+10][N+10][2];
 
string s[2] = {"Taro", "Hanako"};
 
int solve(int a, int b, int c){
    if(grundy[a][b][c] >= 0) return grundy[a][b][c];
    set<int> st;
    if(a>=1 && a+b>2) st.insert(solve(a-1, b, !c));
    if(b>=2 && a+b>2) st.insert(solve(a+1, b-2, c));
    if(a+b==2) st.insert(solve(a, b, !c));
    for(int i = 0; ; i++){
        if(!st.count(i)){
            return (grundy[a][b][c] = i);
        }
    }
}
 
int main(){
    int n;
    cin>>n;
    vector<string> g(n);
    int a = 0;
	int sm = 0;
    UF uf;
    uf.init(n);
    for(int i = 0; i < n; i++){
        cin>>g[i];
        for(int j = 0; j < n; j++){
            if(g[i][j] == 'Y'){
				sm++;
                uf.unite(i, j);
            }
        }
    }
	a ^= (sm/2)&1;
    vector<int> d(n, 0);
    for(int i = 0; i < n; i++){
        d[uf.find(i)]++;
    }
    int sum[2] = {};
    for(int i = 0; i < n; i++){
        int b = d[i];
		if(b==0) continue;
        if(b&2) a ^= 1;
        sum[b%2]++;
    }
    memset(grundy, -1, sizeof(grundy));
    grundy[2][0][0] = grundy[0][2][0] = grundy[1][1][0] =  0;
     
    bool res = solve(sum[0], sum[1], a);
    cout<<(res?s[0]:s[1])<<endl;
 
    return 0;
}