Problem:
Problem H:
DisconnectedGame
Taro と Hanako がゲームをしている.
最初に, 非連結な無向グラフ(二重辺や self loop を含まない) が与えられる. Taro と Hanako は交互に操作を行う. 操作では, 辺で直接つながれていない異なる2 頂点を選び, その間に辺を加える. グラフを連結にしたほうが負けである.
グラフには $V$ 個の頂点がある. $V \times V$ の行列が与えられる. 行列の $(i, j)$-成分が'Y' であるとき $i$ と $j$ の間には辺があり, 'N' であるときは辺が無い.
両者が最善に操作をしたとき, どちらが勝つかを出力せよ.
Constraints
$V$ will be between 2 and 1,000, inclusive.
$a_{i,i}$ will be 'N'.
$a_{i,j}$ will be 'Y' or 'N'.
$a_{i,j}$ will be equal to $a_{j,i}$.
The graph will not be connected.
Input
入力は以下の形式で与えられる:
$V$
$a_{1,1}$ ... $a_{1,V}$
...
$a_{V,1}$ ... $a_{V,V}$
Output
Taro が勝つ場合には "Taro" (quotes for clarity), Hanako が勝つ場合には "Hanako" (quotes for clarity) と 1 行に出力せよ.
Sample Input 1
3
NNN
NNN
NNN
Sample Output 1
Taro
Sample Input 2
5
NNYNN
NNNNN
YNNNN
NNNNY
NNNYN
Sample Output 2
Hanako
Sample Input 3
8
NYNNNNNN
YNNYNNYN
NNNNNNNY
NYNNNNYN
NNNNNNNN
NNNNNNNN
NYNYNNNN
NNYNNNNN
Sample Output 3
Taro

Submitted solutions:

code_snippet: #include <bits/stdc++.h>
using namespace std;

string dp[1009][1009][2];
bool used[1009];

void dfs(int pos, int &cnt, vector<vector<int>> &G) {
    used[pos] = true;
    cnt += 1;
    for (int to : G[pos]) {
        if (used[to] == true) continue;
        dfs(to, cnt, G);
    }
}

string dfs2(int odds, int evens, int turn, int N, int M) {
    if (odds + evens == 1) return "Win";
    if (dp[odds][evens][turn] != "") return dp[odds][evens][turn];

    // Connect
    bool win_flag = false;
    if (odds >= 2) {
        string v1 = dfs2(odds - 2, evens + 1, 1 - turn, N, M);
        if (v1 == "Lose") win_flag = true;
    }
    if ((odds >= 1 && evens >= 1) || evens >= 2) {
        string v2 = dfs2(odds, evens - 1, 1 - turn, N, M);
        if (v2 == "Lose") win_flag = true;
    }

    // Not Connect
    int u = N - odds + 1;
    int maximum_edges = u * (u - 1) / 2;
    if (maximum_edges % 2 != (M + turn) % 2) {
        string v3 = dfs2(odds, evens, 1 - turn, N, M);
        if (v3 == "Lose") win_flag = true;
    }

    // Return
    if (win_flag == true) {
        dp[odds][evens][turn] = "Win";
        return "Win";
    }
    else {
        dp[odds][evens][turn] = "Lose";
        return "Lose";
    }
}

int main() {
    // Step 1. Input
    int N; cin >> N;
    int M = 0;
    vector<string> S(N, "");
    vector<vector<int>> G(N);
    for (int i = 0; i < N; i++) cin >> S[i];
    for (int i = 0; i < N; i++) {
        for (int j = i + 1; j < N; j++) {
            if (S[i][j] == 'N') continue;
            M += 1;
            G[i].push_back(j);
            G[j].push_back(i);
        }
    }

    // Step 2. Easy Case
    if (N % 2 == 1) {
        int a1 = N * (N - 1) / 2;
        int a2 = M;
        cout << ((a1 - a2) % 2 == 1 ? "Taro" : "Hanako") << endl;
        return 0;
    }

    // Step 3. Hard Case: DFS
    int count_odd = 0;
    int count_even = 0;
    for (int i = 0; i < N; i++) {
        if (used[i] == true) continue;
        int cnt = 0;
        dfs(i, cnt, G);
        if (cnt % 2 == 0) count_even += 1;
        if (cnt % 2 == 1) count_odd += 1;
    }
    // cerr << count_even << " " << count_odd << " " << M << endl;

    // Step 4. Hard Case: DP
    string ans = dfs2(count_odd, count_even, 0, N, M);
    cout << (ans == "Win" ? "Taro" : "Hanako") << endl;
    return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 69044
final_rank: 17

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b) for (int i = (int)(a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b)-1; i >= (int)(a); i--)
#define ALL(v) (v).begin(), (v).end()
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) (int)v.size()
#define MIN(v) *min_element(ALL(v))
#define MAX(v) *max_element(ALL(v))
#define LB(v, x) int(lower_bound(ALL(v), (x)) - (v).begin())
#define UB(v, x) int(upper_bound(ALL(v), (x)) - (v).begin())

using uint = unsigned int;
using ll = long long int;
using ull = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
const int inf = 0x3fffffff;
const ll INF = 0x1fffffffffffffff;

template <typename T> inline bool chmax(T &a, T b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T> inline bool chmin(T &a, T b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T, typename U> T ceil(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? (x + y - 1) / y : x / y);
}
template <typename T, typename U> T floor(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? x / y : (x - y + 1) / y);
}
template <typename T> int popcnt(T x) {
    return __builtin_popcountll(x);
}
template <typename T> int topbit(T x) {
    return (x == 0 ? -1 : 63 - __builtin_clzll(x));
}
template <typename T> int lowbit(T x) {
    return (x == 0 ? -1 : __builtin_ctzll(x));
}

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << "P(" << p.first << ", " << p.second << ")";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) {
    os << "{";
    for (int i = 0; i < vec.size(); i++) {
        os << vec[i] << (i + 1 == vec.size() ? "" : ", ");
    }
    os << "}";
    return os;
}
template <typename T, typename U>
ostream &operator<<(ostream &os, const map<T, U> &map_var) {
    os << "{";
    for (auto itr = map_var.begin(); itr != map_var.end(); itr++) {
        os << "(" << itr->first << ", " << itr->second << ")";
        itr++;
        if (itr != map_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const set<T> &set_var) {
    os << "{";
    for (auto itr = set_var.begin(); itr != set_var.end(); itr++) {
        os << *itr;
        ++itr;
        if (itr != set_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
#ifdef LOCAL
#define show(...) _show(0, #__VA_ARGS__, __VA_ARGS__)
#else
#define show(...) true
#endif
template <typename T> void _show(int i, T name) {
    cerr << '\n';
}
template <typename T1, typename T2, typename... T3>
void _show(int i, const T1 &a, const T2 &b, const T3 &...c) {
    for (; a[i] != ',' && a[i] != '\0'; i++)
        cerr << a[i];
    cerr << ":" << b << " ";
    _show(i + 1, a, c...);
}

/**
 * @brief template
 */

struct UnionFind{
    vector<int> par; int n;
    UnionFind(){}
    UnionFind(int _n):par(_n,-1),n(_n){}
    int root(int x){return par[x]<0?x:par[x]=root(par[x]);}
    bool same(int x,int y){return root(x)==root(y);}
    int size(int x){return -par[root(x)];}
    bool unite(int x,int y){
        x=root(x),y=root(y); if(x==y)return false;
        if(size(x)>size(y))swap(x,y);
        par[y]+=par[x]; par[x]=y; n--; return true;
    }
};

/**
 * @brief Union Find
 */

bool Solve() {
    int N;
    cin >> N;
    vector<vector<bool>> B(N,vector<bool>(N));
    rep(i,0,N) {
        rep(j,0,N) {
            char C;
            cin >> C;
            B[i][j] = (C == 'Y');
        }
    }
    int COUNT = 0;
    int A[2] = {};
    UnionFind G(N);
    rep(i,0,N) {
        rep(j,0,i) {
            if (B[i][j]) {
                COUNT++;
                G.unite(i,j);
            }
        }
    }
    rep(i,0,N) {
        if (G.root(i) == i) A[G.size(i)%2]++;
    }
    if (N % 2 == 1) {
        int X = N*(N-1)/2 - COUNT;
        return (X%2==1);
    }
    else {
        int X = N*(N-1)/2 - COUNT;
        if (X % 2 == 0) {
            if (A[0] > 1) return false;
            else if (A[1] == 2) return true;
            else if (A[1] == 4) {
                int Y = 0;
                rep(i,0,N) {
                    if (G.root(i) == i) {
                        Y += (G.size(i) * (G.size(i)-1)) / 2;
                    }
                }
                Y -= COUNT;
                return (Y % 2 == 1);
            }
            else return false;
        }
        else {
            if (A[0] >= 1) return true;
            else if (A[1] == 2) return false;
            else if (A[1] == 4) {
                int Y = 0;
                rep(i,0,N) {
                    if (G.root(i) == i) {
                        Y += (G.size(i) * (G.size(i)-1)) / 2;
                    }
                }
                Y -= COUNT;
                return (Y % 2 == 1);
            }
            else return true;
        }
    }
}

int main() {
    cout << (Solve() ? "Taro" : "Hanako") << endl;
}
accuracy: 1
time_ms: 20
memory_kb: 3484
final_rank: 2

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b) for (int i = (int)(a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b)-1; i >= (int)(a); i--)
#define ALL(v) (v).begin(), (v).end()
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) (int)v.size()
#define MIN(v) *min_element(ALL(v))
#define MAX(v) *max_element(ALL(v))
#define LB(v, x) int(lower_bound(ALL(v), (x)) - (v).begin())
#define UB(v, x) int(upper_bound(ALL(v), (x)) - (v).begin())

using uint = unsigned int;
using ll = long long int;
using ull = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
const int inf = 0x3fffffff;
const ll INF = 0x1fffffffffffffff;

template <typename T> inline bool chmax(T &a, T b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T> inline bool chmin(T &a, T b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T, typename U> T ceil(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? (x + y - 1) / y : x / y);
}
template <typename T, typename U> T floor(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? x / y : (x - y + 1) / y);
}
template <typename T> int popcnt(T x) {
    return __builtin_popcountll(x);
}
template <typename T> int topbit(T x) {
    return (x == 0 ? -1 : 63 - __builtin_clzll(x));
}
template <typename T> int lowbit(T x) {
    return (x == 0 ? -1 : __builtin_ctzll(x));
}

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << "P(" << p.first << ", " << p.second << ")";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) {
    os << "{";
    for (int i = 0; i < vec.size(); i++) {
        os << vec[i] << (i + 1 == vec.size() ? "" : ", ");
    }
    os << "}";
    return os;
}
template <typename T, typename U>
ostream &operator<<(ostream &os, const map<T, U> &map_var) {
    os << "{";
    for (auto itr = map_var.begin(); itr != map_var.end(); itr++) {
        os << "(" << itr->first << ", " << itr->second << ")";
        itr++;
        if (itr != map_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const set<T> &set_var) {
    os << "{";
    for (auto itr = set_var.begin(); itr != set_var.end(); itr++) {
        os << *itr;
        ++itr;
        if (itr != set_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
#ifdef LOCAL
#define show(...) _show(0, #__VA_ARGS__, __VA_ARGS__)
#else
#define show(...) true
#endif
template <typename T> void _show(int i, T name) {
    cerr << '\n';
}
template <typename T1, typename T2, typename... T3>
void _show(int i, const T1 &a, const T2 &b, const T3 &...c) {
    for (; a[i] != ',' && a[i] != '\0'; i++)
        cerr << a[i];
    cerr << ":" << b << " ";
    _show(i + 1, a, c...);
}

/**
 * @brief template
 */

struct UnionFind{
    vector<int> par; int n;
    UnionFind(){}
    UnionFind(int _n):par(_n,-1),n(_n){}
    int root(int x){return par[x]<0?x:par[x]=root(par[x]);}
    bool same(int x,int y){return root(x)==root(y);}
    int size(int x){return -par[root(x)];}
    bool unite(int x,int y){
        x=root(x),y=root(y); if(x==y)return false;
        if(size(x)>size(y))swap(x,y);
        par[y]+=par[x]; par[x]=y; n--; return true;
    }
};

/**
 * @brief Union Find
 */

bool Solve() {
    int N;
    cin >> N;
    vector<vector<bool>> B(N,vector<bool>(N));
    rep(i,0,N) {
        rep(j,0,N) {
            char C;
            cin >> C;
            B[i][j] = (C == 'Y');
        }
    }
    int COUNT = 0;
    int A[2] = {};
    UnionFind G(N);
    rep(i,0,N) {
        rep(j,0,i) {
            if (B[i][j]) {
                COUNT++;
                G.unite(i,j);
            }
        }
    }
    rep(i,0,N) {
        if (G.root(i) == i) A[G.size(i)%2]++;
    }
    if (N % 2 == 1) {
        int X = N*(N-1)/2 - COUNT;
        return (X%2==1);
    }
    else {
        int X = N*(N-1)/2 - COUNT;
        if (X % 2 == 0) {
            if (A[0] > 1) return false;
            else if (A[1] == 2) return true;
            else if (A[1] == 4) {
                int Y = 0;
                rep(i,0,N) {
                    if (G.root(i) == i) {
                        Y += (G.size(i) * (G.size(i)-1)) / 2;
                    }
                }
                Y -= COUNT;
                return (Y % 2 == 1);
            }
            else return false;
        }
        else {
            if (A[0] >= 1) return true;
            else if (A[1] == 2) return false;
            else if (A[1] == 4) {
                int Y = 0;
                rep(i,0,N) {
                    if (G.root(i) == i) {
                        Y += (G.size(i) * (G.size(i)-1)) / 2;
                    }
                }
                Y -= COUNT;
                return (Y % 2 == 0);
            }
            else return true;
        }
    }
}

int main() {
    cout << (Solve() ? "Taro" : "Hanako") << endl;
}
accuracy: 1
time_ms: 20
memory_kb: 3512
final_rank: 4

code_snippet: #include <bits/stdc++.h>

using namespace std;
 
#define REP(i, n) for (ll i = 0; i < n; i++)
#define REPR(i, n) for (ll i = n; i >= 0; i--)
#define FOR(i, m, n) for (ll i = m; i < n; i++)
#define FORR(i, m, n) for (ll i = m; i >= n; i--)
#define REPO(i, n) for (ll i = 1; i <= n; i++)
#define ll long long
#define INF (ll)(1ll << 62)
#define MINF (-1 * INF)
#define ALL(n) n.begin(), n.end()
#define MOD 1000000007
#define P pair<ll, ll>

vector<ll> g[1100];
bool used[1100], Taro_even;
ll n, cnt, even, odd, now;

void dfs(ll a){
    used[a] = true;
    now++;
    for (auto& i : g[a]) {
        if(!used[i])dfs(i);
    }
}
int main(){

    cin >> n;
    REP(i, n){
        REP(j, n){
            char a;
            cin >> a;
            if(a == 'Y') {
                g[i].push_back(j);
                cnt++;
            }
        }
    }
    cnt /= 2;
    if(n % 2 == 1){
        cout << ((n * (n - 1) / 2 - cnt) % 2 == 0 ? "Hanako" : "Taro") <<endl;
        return 0;
    }
    REP(i, n){
        if(!used[i]){
            now = 0;
            dfs(i);
            if(now % 2 == 0)even++;
            else odd++;
        }
    }
    if((n * (n - 1) / 2 - cnt) % 2 == 1)  Taro_even = true;
    if(even == 0){
        if(Taro_even) cout << "Hanako" <<endl;
        else{
            if(odd == 2)cout << "Taro"<<endl;
            else cout << "Hanako"<<endl;
        }
    }
    else if(even == 1)cout << "Taro"<<endl;
    else cout << (Taro_even ? "Taro" : "Hanako")<<endl;
}
accuracy: 1
time_ms: 20
memory_kb: 3512
final_rank: 4

code_snippet: #include "bits/stdc++.h"

using namespace std;

int main(){
    int N;
    cin>>N;
    vector<string>edge(N);
    int M=0;
    for(int i=0;i<N;i++){
        cin>>edge[i];
        for(auto j:edge[i]){
            if(j=='Y')M++;
        }
    }
    M/=2;
    if(N&1){
        int num=N%4==3;
        if((M&1)!=num){
            cout<<"Taro"<<endl;
        }
        else{
            cout<<"Hanako"<<endl;
        }
        return 0;
    }
    vector<int>num(2);
    vector<int>p(N,-1);
    for(int i=0;i<N;i++){
        if(p[i]!=-1)continue;
        p[i]=i;
        queue<int>Q;
        Q.push(i);
        int n=0;
        while(!Q.empty()){
            int cn=Q.front();
            Q.pop();
            n++;
            for(int j=0;j<N;j++){
                if(edge[cn][j]=='Y'){
                    if(p[j]==-1){
                        p[j]=i;
                        Q.push(j);
                    }
                }
            }
        }
        num[n&1]++;
    }
    vector<vector<int>>win(num[0]+num[1]+1,vector<int>(num[0]+num[1]+1));
    int fst=num[0]+num[1];
    fst&=1;
    if(N%4==0){
        if(M&1){
            win[2][0]=1;
        }
        else{
            win[0][2]=1;
        }
    }
    else{
        if(M&1){
            win[0][2]=1;
        }
        else{
            win[2][0]=1;
        }
    }
    for(int i=3;i<=num[0]+num[1];i++){
        for(int j=0;j<=num[0]+num[1];j++){
        //    cout<<i<<" "<<j<<endl;
            int k=i-j;
            if(k<0||k>num[0]+num[1])continue;
            if(fst){
                if(j>=2){
                    win[j][k]|=win[j-1][k];
                }
                if(j>=1&&k>=1){
                    win[j][k]|=win[j-1][k];
                }
                if(k>=2){
                    win[j][k]|=win[j+1][k-2];
                }
            }
            else{
                int cnt=0;
                win[j][k]=1;
                if(j>=2){
                    win[j][k]&=win[j-1][k];
                    cnt++;
                }
                if(j>=1&&k>=1){
                    win[j][k]&=win[j-1][k];
                    cnt++;
                }
                if(k>=2){
                    win[j][k]&=win[j+1][k-2];
                    cnt++;
                }
                if(cnt==0)win[j][k]=0;
            }
      //      cout<<j<<" "<<k<<" "<<win[j][k]<<endl;
        }
        fst^=1;
    }
 //   cout<<num[0]<<" "<<num[1]<<" "<<M<<endl;
    if(win[num[0]][num[1]])cout<<"Taro"<<endl;
    else cout<<"Hanako"<<endl;
}
accuracy: 1
time_ms: 10
memory_kb: 9000
final_rank: 10

code_snippet: #include <bits/stdc++.h>
#define ll long long
#define rep(i, n) for(ll i = 0; i < (n); i++)
#define rep2(i, a, b) for(ll i = (a); i < (b); i++)
#define vi vector<int>
using namespace std;

int main() {
    int n;
    cin >> n;

    vector a(n, vi(n));
    rep(i, n) rep(j, n) {
        char c;
        cin >> c;
        a[i][j] = (c == 'Y');
    }

    vi v(n);

    vi c(2);

    int sum = 0;
    rep(i, n) {
        if(!v[i]) {
            queue<int> q;
            q.emplace(i);
            v[i] = 1;
            int cnt = 0;
            while(!empty(q)) {
                cnt += 1;
                int x = q.front();
                q.pop();
                rep(j, n) if(a[x][j] and !v[j]) {
                    v[j] = true;
                    q.emplace(j);
                }
            }
            c[cnt & 1]++;
            sum += cnt * (cnt - 1) / 2;
        }
    }
    rep(i, n) rep(j, i) sum -= a[i][j];
    sum &= 1;

    vector dp(n + 1, vector(n + 1, vi(2)));
    rep2(m, 1, n + 1) rep(i, m + 1) rep(k, 2) {
        int j = m - i;
        int f = false;
        if(i >= 2) { f |= !dp[i - 1][j][k ^ 1]; }
        if(i and j) f |= !dp[i - 1][j][k ^ 1];
        if(j >= 2) f |= !dp[i + 1][j - 2][k];
        if(k) f |= !dp[i][j][0];
        dp[i][j][k] = f;
        if(m == 2 and !k) dp[i][j][k] = 0;
    }
    cout << (dp[c[0]][c[1]][sum] ? "Taro" : "Hanako") << endl;
}
accuracy: 1
time_ms: 70
memory_kb: 61920
final_rank: 18

code_snippet: #pragma GCC target("avx2")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#include<bits/stdc++.h>
// #include<atcoder/mincostflow>
using namespace std;
// using namespace atcoder;

struct UnionFind{
	int n;
	vector<int> par;
	UnionFind(int n): n(n){
		par.assign(n, -1);
	};

	int find(int x){
		if(par[x] < 0) return x;
		par[x] = find(par[x]);
		return par[x];
	}

	bool same(int x, int y){
		return find(x) == find(y);
	}

	void unite(int x, int y){
		x = find(x);
		y = find(y);
		if(x == y) return;
		if(par[x] > par[y]) swap(x, y);
		par[x] += par[y];
		par[y] = x;		
	}

	int size(int x){
		return -par[find(x)];
	}

	bool isroot(int x){
		return x == find(x);
	}
};

void solve(){
	int n;
	cin >> n;
	UnionFind UF(n);
	vector<vector<char>> A(n, vector<char>(n));
	int cnt = 0;
	for(int i = 0; i < n; i++) for(int j = 0; j < n; j++){
		cin >> A[i][j];
		if(A[i][j] == 'Y'){
			UF.unite(i, j);
			cnt++;
		}
	}
	cnt /= 2;
	int all_ = n * (n - 1) / 2;
	
	int r = all_ - cnt;
	if(n & 1){
		if(r & 1) cout << "Taro\n";
		else cout << "Hanako\n";
		return;
	}
	

	int odd = 0;
	int even = 0;
	cnt &= 1;
	for(int i = 0; i < n; i++){
		if(UF.isroot(i)){
			int sz = UF.size(i);
			if(sz & 1) odd++;
			else even++;
			cnt += sz * (sz - 1) / 2;
			cnt &= 1;
		}
	}


	vector<vector<vector<int>>> memo(odd + 1, vector<vector<int>>(odd + even + 1, vector<int>(2, -1)));
	auto solve=[&memo, &all_](auto self, int odd, int even, int t, int cnt) -> int {		
		if(memo[odd][even][cnt] != -1) return memo[odd][even][cnt];
		if(odd + even == 2){
			if(cnt == 1) return t;
			else return t ^ 1;
		}
		int ret;
		if(t == 0){
			ret = 1;
			if(odd >= 1 && even >= 1){
				if(self(self, odd, even - 1, t ^ 1, cnt ^ 1) == 0) ret = 0;
			}
			if(odd >= 2){
				if(self(self, odd - 2, even + 1, t ^ 1, cnt) == 0) ret = 0;
			}
			if(even >= 2){
				if(self(self, odd, even - 1, t ^ 1, cnt ^ 1) == 0) ret = 0;
			}
			if(cnt == 1){
				if(self(self, odd, even, t ^ 1, cnt ^ 1) == 0) ret = 0;
			}
		}
		else{
			ret = 0;
			if(odd >= 1 && even >= 1){
				if(self(self, odd, even - 1, t ^ 1, cnt ^ 1) == 1) ret = 1;
			}
			if(odd >= 2){
				if(self(self, odd - 2, even + 1, t ^ 1, cnt) == 1) ret = 1;
			}
			if(even >= 2){
				if(self(self, odd, even - 1, t ^ 1, cnt ^ 1) == 1) ret = 1;
			}
			if(cnt == 1){
				if(self(self, odd, even, t ^ 1, cnt ^ 1) == 1) ret = 1;
			}
		}
		memo[odd][even][cnt] = ret;
		return ret;
	};
	int ans = solve(solve, odd, even, 0, cnt);
	if(ans == 0) cout << "Taro\n";
	else cout << "Hanako\n";
}

int main(){
	cin.tie(0)->sync_with_stdio(0);
	int t;
	t = 1;
	// cin >> t;
	while(t--) solve();
}
accuracy: 1
time_ms: 50
memory_kb: 58928
final_rank: 15

code_snippet: // 2021.12.2
// #define _GLIBCXX_DEBUG
// #define __USE_MATH_DEFINES // const double PI = acos(-1.0)
#include <bits/stdc++.h>
using namespace std;
// (*) snipet
// type name
using ll = long long;
using pii = pair<int, int>; using pil = pair<int, ll>; using pli = pair<ll, int>; using pll = pair<ll, ll>;
using vi = vector<int>; using vvi = vector<vi>; using vvvi = vector<vvi>;
using vl = vector<ll>; using vvl = vector<vl>; using vvvl = vector<vvl>;
using vpii = vector<pii>; using vpil = vector<pil>; using vpli = vector<pli>; using vpll = vector<pll>;
using vb = vector<bool>; using vvb = vector<vb>; using vvvb = vector<vvb>;
using vd = vector<double>; using vvd = vector<vd>; using vvvd = vector<vvd>;
template <typename T> using pq = priority_queue<T, vector<T>>;
template <typename T> using pql = priority_queue<T, vector<T>, greater<T>>;
// rep
#define rep0(goal) for (int cnt = 0; cnt < int(goal); ++cnt)
#define rep(cnt, goal) for (int cnt = 0; cnt < int(goal); ++cnt)
#define rep2(cnt, start, goal) for (int cnt = int(start); cnt < int(goal); ++cnt)
#define rep3(cnt, start, goal) for (int cnt = int(start); cnt > int(goal); --cnt)
// all
#define all(ctn) begin(ctn), end(ctn)
// chmax, chmin
template <typename T> bool chmax(T &x, const T y) { if (x < y) { x = y; return true; } else { return false; }}
template <typename T> bool chmin(T &x, const T y) { if (x > y) { x = y; return true; } else { return false; }}
// etc.
void Yn(const bool &exp) { if (exp) cout << "Yes" << endl; else cout << "No" << endl;}
void set_prec(const int &dig) { cout << fixed << setprecision(dig); cerr << fixed << setprecision(dig);}
// template <typename T> T INF() { return numeric_limits<T>::max();}
// template <typename T> T MIN_INF() { return numeric_limits<T>::min();}

// #include <atcoder/all>
// using namespace atcoder;
// // using mint = atcoder::modint1000000007;
// using mint = atcoder::modint998244353;
// // using mint = atcoder::modint;
// void pr(const mint &MINT) { cerr << MINT.val(); } // mint
// using pmm = pair<mint, mint>; using pim = pair<int, mint>; using pmi = pair<mint, int>; using plm = pair<ll, mint>; using pml = pair<mint, ll>;
// using vm = vector<mint>; using vvm = vector<vm>; using vvvm = vector<vvm>;

struct dsu {
  public:
    dsu() : _n(0) {}
    explicit dsu(int n) : _n(n), parent_or_size(n, -1) {}

    int merge(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        int x = leader(a), y = leader(b);
        if (x == y) return x;
        if (-parent_or_size[x] < -parent_or_size[y]) std::swap(x, y);
        parent_or_size[x] += parent_or_size[y];
        parent_or_size[y] = x;
        return x;
    }

    bool same(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        return leader(a) == leader(b);
    }

    int leader(int a) {
        assert(0 <= a && a < _n);
        if (parent_or_size[a] < 0) return a;
        return parent_or_size[a] = leader(parent_or_size[a]);
    }

    int size(int a) {
        assert(0 <= a && a < _n);
        return -parent_or_size[leader(a)];
    }

    std::vector<std::vector<int>> groups() {
        std::vector<int> leader_buf(_n), group_size(_n);
        for (int i = 0; i < _n; i++) {
            leader_buf[i] = leader(i);
            group_size[leader_buf[i]]++;
        }
        std::vector<std::vector<int>> result(_n);
        for (int i = 0; i < _n; i++) {
            result[i].reserve(group_size[i]);
        }
        for (int i = 0; i < _n; i++) {
            result[leader_buf[i]].push_back(i);
        }
        result.erase(
            std::remove_if(result.begin(), result.end(),
                           [&](const std::vector<int>& v) { return v.empty(); }),
            result.end());
        return result;
    }

  private:
    int _n;
    // root node: -1 * component size
    // otherwise: parent
    std::vector<int> parent_or_size;
};

// debug
template <typename T> void pr(const T &obj) { cerr << obj; } // single
template <typename T, typename ...Ts> void pr(const T &first, const Ts &...rest) { pr(first); cerr << ", "; pr(rest...); } // multi(plural)
template <typename S, typename T> void pr(const pair<S, T> &PAIR) { cerr << "("; pr(PAIR.first); cerr << ", "; pr(PAIR.second); cerr << ")"; } // pair
template <typename S, typename T, typename U> void pr(const tuple<S, T, U> &trp) { cerr << "("; pr(get<0>(trp)); cerr << ", "; pr(get<1>(trp)); cerr << ", "; pr(get<2>(trp)); cerr << ")"; } // tuple(3)
template <typename T> void pr(const vector<T> &vec) { cerr << "{"; for (T obj : vec) { pr(obj); cerr << ", "; } cerr << "}"; } // vector
template <typename T> void pr(const vector<vector<T>> &vv) { rep(row, vv.size()) { cerr << endl; cerr << "[" << row << "]: "; pr(vv[row]); }} // vector(multi-D)
template <typename T> void pr(const set<T> &SET) { cerr << "{"; for (T obj : SET) { pr(obj); cerr << ", "; } cerr << "}"; } // set
template <typename T> void pr(const multiset<T> &MS) { cerr << "{"; for (T obj : MS) { pr(obj); cerr << ", "; } cerr << "}"; } // multiset
template <typename S, typename T> void pr(const map<S, T> &MAP) { cerr << "{"; for (pair<S, T> p : MAP) { pr(p.first); cerr << ": "; pr(p.second); cerr << ", "; } cerr << "}";} // map
template <typename T> void pr(const queue<T> &que) { queue<T> q = que; cerr << "{"; while (!q.empty()) { pr(q.front()); q.pop(); cerr << ", ";} cerr << "}";} // queue
template <typename T> void pr(const deque<T> &deq) { deque<T> d = deq; cerr << "{"; while (!d.empty()) { pr(d.front()); d.pop_front(); cerr << ", ";} cerr << "}";} // deque
template <typename T> void pr(const stack<T> &stc) { stack<T> s = stc; vector<T> v; while (!s.empty()) { v.push_back(s.top()); s.pop();} reverse(all(v)); cerr << "{"; for (T obj : v) cerr << obj << ", "; cerr << "}";} // stack
template <typename T> void pr(const priority_queue<T> &pq) { priority_queue<T> p = pq; cerr << "{"; while (!p.empty()) { pr(p.top()); p.pop(); cerr << ", "; } cerr << "}";} // priority_queue
template <typename T> void pr(const priority_queue<T, vector<T>, greater<T>> &pq) { priority_queue<T, vector<T>, greater<T>> p = pq; cerr << "{"; while (!p.empty()) { pr(p.top()); p.pop(); cerr << ", ";} cerr << "}";} // priority_queue(from less)

#define db(obj) cerr << #obj << ": "; pr(obj); cerr << " ";
#define dl(obj) db(obj); cerr << endl;
#define dm(...) cerr << "(" << #__VA_ARGS__ << "): ("; pr(__VA_ARGS__); cerr << ") ";
#define dml(...) dm(__VA_ARGS__); cerr << endl;

// 連結になる直前の2つの連結成分の頂点の数(u, V-u)を考える
// u*(u-1)/2 + (V-u)*(V-u-1)/2 まで辺を引ける
// この値はVが奇数(1,3 mod 4)なら一定、
// Vが偶数(0,2 mod 4)ならuの偶奇によって異なる。
// (頂点数が偶数である連結成分の数, 頂点数が奇数である連結成分の数, 連結成分の数を変化させずに引くことのできる辺の本数の偶奇) を状態とする
// (2,0,d) と (0,2,d) のいずれに至るか
// (1) 偶数同士をまとめる (x,y) -> (x-1, y) (x>=2) 辺は 0*0-1 = 1 増える(mod2)
// (2) 偶数と奇数をまとめる (x,y) -> (x-1, y) (x>=1, y>=1) 辺は 0*1-1 = 1 増える(mod2)
// (3) 奇数同士をまとめる (x,y) -> (x+1, y-2) (y>=2) 辺は 1*1-1 = 0 増える(mod2)

// global
int V;
vector<string> a;

dsu d;
int n0 = 0, n1 = 0, e = 0;

map<tuple<int, int, int>, int> dp;

void input() {
	cin >> V;
	a = vector<string>(V);
	rep(i, V) cin >> a[i];
}

void init() {
	input();
	d = dsu(V);
	rep(i, V) rep2(j, i + 1, V) {
		if (a[i][j] == 'Y') {
			d.merge(i, j);
			++e;
		}
	}
	for (vi g : d.groups()) {
		if (g.size() % 2 == 0) ++n0;
		else ++n1;
	}
	// dl(d.groups()); //
	// dm(n0, n1); dl(e);
}

bool rec(int n0, int n1, int r) {
	// dml(n0, n1, r); dl(dp); //
	assert(n0 >= 0); assert(n1 >= 0); assert(n0 + n1 >= 2); assert(r == 0 || r == 1);

	if (dp.count(make_tuple(n0, n1, r))) return dp[make_tuple(n0, n1, r)];
	if (n0 + n1 == 2 && r == 0) return dp[make_tuple(n0, n1, r)] = 0; // 負け
	if (n0 + n1 == 2 && r == 1) return dp[make_tuple(n0, n1, r)] = 1; // 勝ち

	// 原則負け、負けの状態に至る手があるかを探す
	// 連結成分の個数を変えずに辺を引く -> 無益?
	// if (!rec(n0, n1, 1 - r)) return dp[make_tuple(n0, n1, r)] = 1;
	// 偶数同士をまとめる
	if (n0 >= 2 && !rec(n0 - 1, n1, 1 - r)) return dp[make_tuple(n0, n1, r)] = 1;
	// 偶数と奇数をまとめる
	if (n0 >= 1 && n1 >= 1 && !rec(n0 - 1, n1, 1 - r)) return dp[make_tuple(n0, n1, r)] = 1;
	// 奇数同士をまとめる
	if (n1 >= 2 && !rec(n0 + 1, n1 - 2, r)) return dp[make_tuple(n0, n1, r)] = 1;
	// 負け
	return dp[make_tuple(n0, n1, r)] = 0;
}

void solve() {
	int cw = 0;
	for (vi g : d.groups()) {
		int s = g.size();
		cw += s * (s - 1) / 2;
	}
	// cout << rec(n0, n1, (cw - e) % 2) << endl;
	// dl(dp); //

	cout << (rec(n0, n1, (cw - e) % 2) ? "Taro" : "Hanako") << endl;
};

void test() {
}

int main() {
	init();
	// test(); //
	solve();
}
accuracy: 1
time_ms: 20
memory_kb: 7548
final_rank: 11

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, s, t) for (int i = (int)(s); i < (int)(t); ++i)
#define revrep(i, t, s) for (int i = (int)(t)-1; i >= (int)(s); --i)
#define all(x) begin(x), end(x)
template <typename T> bool chmax(T& a, const T& b) { return a < b ? (a = b, 1) : 0; }
template <typename T> bool chmin(T& a, const T& b) { return a > b ? (a = b, 1) : 0; }

class UnionFind {
public:
    UnionFind() = default;
    explicit UnionFind(int n) : data(n, -1) {}

    int find(int x) {
        if (data[x] < 0) return x;
        return data[x] = find(data[x]);
    }

    void unite(int x, int y) {
        x = find(x);
        y = find(y);
        if (x == y) return;
        if (data[x] > data[y]) std::swap(x, y);
        data[x] += data[y];
        data[y] = x;
    }

    bool same(int x, int y) {
        return find(x) == find(y);
    }

    int size(int x) {
        return -data[find(x)];
    }

private:
    std::vector<int> data;
};

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout << fixed << setprecision(15);

    int V;
    cin >> V;
    vector<string> a(V);
    for (auto& x : a) cin >> x;
    int rem = V*(V-1)/2;
    int r = 0;
    UnionFind uf(V);
    rep(i,0,V) rep(j,i+1,V) {
        if (a[i][j] == 'Y') {
            --rem;
            --r;
            uf.unite(i, j);
        }
    }
    vector<int> cnt(2);
    rep(i,0,V) if (uf.find(i) == i) {
        ++cnt[uf.size(i) % 2];
        r += uf.size(i) * (uf.size(i)-1) / 2;
    }
    r %= 2;

    map<tuple<int, int, int>, bool> memo;
    auto dfs = [&](auto& dfs, int cnt0, int cnt1, int r) -> bool {
        if (cnt0 + cnt1 == 2) {
            if (r) return true;
            else return false;
        }
        if (memo.count({cnt0, cnt1, r})) return memo[{cnt0, cnt1, r}];
        bool win = false;
        if (r) win |= !dfs(dfs, cnt0, cnt1, 0);
        if (cnt0 > 0) win |= !dfs(dfs, cnt0-1, cnt1, (r+1)%2);
        if (cnt1 > 1) win |= !dfs(dfs, cnt0+1, cnt1-2, r);
        return memo[{cnt0, cnt1, r}] = win;
    };

    cout << (dfs(dfs, cnt[0], cnt[1], r) ? "Taro" : "Hanako") << endl;
}
accuracy: 1
time_ms: 140
memory_kb: 20184
final_rank: 12

code_snippet: #pragma GCC optimize("Ofast")
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <cstdio>
#include <ctime>
#include <assert.h>
#include <chrono>
#include <random>
#include <numeric>
#include <set>
#include <deque>
#include <stack>
#include <sstream>
#include <utility>
#include <cstring>
#include <unordered_map>
#include <unordered_set>
#include <tuple>
#include <array>
#include <bitset>
using namespace std;
typedef long long int ll;
typedef unsigned long long ull;

mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
ll myRand(ll B) {
    return (ull)rng() % B;
}
inline double time() {
    return static_cast<long double>(chrono::duration_cast<chrono::nanoseconds>(chrono::steady_clock::now().time_since_epoch()).count()) * 1e-9;
}

// 嘘解法+chalケース
// https://onlinejudge.u-aizu.ac.jp/solutions/problem/2376/review/4908870/KKT89/C++14

struct UnionFind{
    vector<int> par,num;
    UnionFind(int n):par(n),num(n,1){
        iota(par.begin(),par.end(),0);  //include<numeric>
    }
    int find(int v){
        return (par[v]==v)?v:(par[v]=find(par[v]));
    }
    void unite(int u,int v){
        u=find(u),v=find(v);
        if(u==v)return;
        if(num[u]<num[v])swap(u,v);
        num[u]+=num[v];
        par[v]=u;
    }
    bool same(int u,int v){
        return find(u) == find(v);
    }
    int size(int v){
        return num[find(v)];
    }
};

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    int n; cin >> n;
    vector<string> g(n);
    UnionFind uf(n);
    for(int i=0;i<n;i++){
        cin >> g[i];
        for(int j=0;j<n;j++){
            if(g[i][j] == 'Y'){
                uf.unite(i,j);
            }
        }
    }
    vector<int> e(n),v(n);
    for(int i=0;i<n;i++){
        v[uf.find(i)]++;
        for(int j=i+1;j<n;j++){
            if(g[i][j] == 'Y'){
                e[uf.find(i)]++;
            }
        }
    }
    int od = 0;
    int ev = 0;
    int rm = 0;
    for(int i=0;i<n;i++){
        if(uf.find(i) == i){
            if(v[i]%2) od++;
            else ev++;
            rm += v[i]*(v[i]-1)/2 - e[i];
        }
    }
    rm %= 2;

    using T = tuple<int,int,int>;
    map<T,bool> mp;

    auto solve=[&](auto solve,int o,int e,int r)->bool{
        if(mp.find(T(o,e,r)) != mp.end()) return mp[T(o,e,r)];
        bool res = false;
        // o+eの最小値は2(連結にした時点でゲーム終了なので)
        if(o+e == 2){
            if(r%2 == 0) res = false;
            else res = true;
        }
        else{
            // 全ての行動を試せば良い
            // 遷移が実はDAGになっていそう
            if(r == 1){
                if(!solve(solve,o,e,0)) res = true;
            }
            if(o >= 2){
                if(!solve(solve,o-2,e+1,r)) res = true;
            }
            if(e >= 2){
                if(!solve(solve,o,e-1,r^1)) res = true;
            }
            if(o and e){
                if(!solve(solve,o,e-1,r^1)) res = true;
            }
        }

        mp[T(o,e,r)] = res; 
        return res;
    };
    if(solve(solve, od, ev, rm)){
        cout << "Taro" << endl;
    }
    else{
        cout << "Hanako" << endl;
    }
}
accuracy: 1
time_ms: 320
memory_kb: 20352
final_rank: 19

code_snippet: #include <bits/stdc++.h>
using namespace std;

struct UnionFind {
    vector<int> par;
    vector<int> size;
    UnionFind(int n) {
        par.resize(n);
        size.resize(n,1);
        for(int i = 0; i < n; i++) {
            par[i] = i;
        }
    }
    int find(int x) {
        if(par[x] == x) {
            return x;
        }
        return par[x] = find(par[x]);
    }
    bool same(int x, int y) {
        return find(x) == find(y);
    }
    int consize(int x) {
        return size[find(x)];
    }
    bool unite(int x, int y) {
        x = find(x);
        y = find(y);
        if(x == y) {
            return false;
        }
        if(size[x] > size[y]) {
            swap(x,y);
        }
        par[x] = y;
        size[y] += size[x];
        return true;
    }
};

int main() {
    int V;
    cin >> V;
    int cnt = 0;
    UnionFind uf(V);
    for(int i = 0; i < V; i++) {
        for(int j = 0; j < V; j++) {
            char a;
            cin >> a;
            if(a == 'Y') {
                uf.unite(i,j);
                cnt++;
            }
        }
    }
    cnt /= 2;
    if(V%2 == 1) {
        cout << (((V*(V-1)/2-cnt)%2 == 1)?"Taro":"Hanako") << endl;
    }
    else {
        vector<int>tmp;
        for(int i = 0; i < V; i++) {
            if(uf.find(i) == i) {
                tmp.push_back(uf.consize(i)%2);
            }
        }
        sort(tmp.begin(),tmp.end());
        if((V*(V-1)/2-cnt)%2 == 0) {
            if(tmp.size() == 2) {
                if(tmp[1] == 0) {
                    cout << "Hanako" << endl;
                }
                else {
                    cout << "Taro" << endl;
                }
                return 0;
            }
            if(tmp.size()%2 == 0) {
                cout << "Hanako" << endl;
                return 0;
            }
        }
        else {
            if(tmp.size() == 2) {
                if(tmp[1] == 1) {
                    cout << "Hanako" << endl;
                }
                else {
                    cout << "Taro" << endl;
                }
                return 0;
            }
            if(tmp.size()%2 == 1) {
                cout << "Taro" << endl;
                return 0;
            }
        }
        if(tmp.size() == 3 && tmp[2] == 0) {
            cout << "Hanako" << endl;
        }
        else if(tmp.size() == 3) {
            cout << "Taro" << endl;
        }
        if(tmp.size() == 4 && (tmp[3] == 0 || (tmp[1] == 0 && tmp[2] == 1))) {
            cout << "Taro" << endl;
        }
        else if(tmp.size() == 4) {
            cout << "Hanako" << endl;
        }
        if(tmp.size() >= 5) {
            if(tmp.size()%2 == 1) {
                if(tmp[0] == 0 && tmp[1] == 1) {
                    cout << "Taro" << endl;
                }
                else {
                    cout << "Hanako" << endl;
                }
            }
            else {
                if(tmp[0] == 1) {
                    cout << "Hanako" << endl;
                }
                else {
                    cout << "Taro" << endl;
                }
            }
        }
    }
}
accuracy: 1
time_ms: 20
memory_kb: 3488
final_rank: 3

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, s, t) for (int i = (int)(s); i < (int)(t); ++i)
#define revrep(i, t, s) for (int i = (int)(t)-1; i >= (int)(s); --i)
#define all(x) begin(x), end(x)
template <typename T> bool chmax(T& a, const T& b) { return a < b ? (a = b, 1) : 0; }
template <typename T> bool chmin(T& a, const T& b) { return a > b ? (a = b, 1) : 0; }

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout << fixed << setprecision(15);

    int V;
    cin >> V;
    vector<string> G(V);
    for (auto& x : G) cin >> x;
    vector<bool> visited(V);
    int a = 0, b = 0, c = 0;
    rep(i,0,V) {
        if (visited[i]) continue;
        visited[i] = true;
        vector<int> vs;
        stack<int> st;
        st.push(i);
        while (!st.empty()) {
            int v = st.top();
            st.pop();
            vs.push_back(v);
            rep(u,0,V) if (G[v][u] == 'Y' && !visited[u]) {
                visited[u] = true;
                st.push(u);
            }
        }
        if (vs.size() % 2) ++a;
        else ++b;
        int cnt = vs.size()*(vs.size()-1)/2;
        for (int u : vs) for (int v : vs) if (u < v && G[u][v] == 'Y') --cnt;
        c ^= cnt % 2;
    }
    vector<vector<vector<int>>> memo(V+1, vector<vector<int>>(V+1, vector<int>(2, -1)));

    auto dfs = [&](auto& dfs, int a, int b, int c) -> bool {
        if (a < 0 || b < 0 || a + b < 2) return true;
        if (a + b == 2 && c == 0) return false;
        if (memo[a][b][c] != -1) return memo[a][b][c] == 1;
        bool win = !dfs(dfs, a-2, b+1, c) | !dfs(dfs, a, b-1, c^1);
        if (c) {
            win |= !dfs(dfs, a, b, 0);
        }
        memo[a][b][c] = win;
        return win;
    };

    cout << (dfs(dfs, a, b, c) ? "Taro" : "Hanako") << endl;
}
accuracy: 1
time_ms: 50
memory_kb: 58936
final_rank: 16

code_snippet: #include <array>
#include <iostream>
#include <map>

#include <algorithm>
#include <cassert>
#include <vector>

namespace atcoder {

// Implement (union by size) + (path compression)
// Reference:
// Zvi Galil and Giuseppe F. Italiano,
// Data structures and algorithms for disjoint set union problems
struct dsu {
  public:
    dsu() : _n(0) {}
    explicit dsu(int n) : _n(n), parent_or_size(n, -1) {}

    int merge(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        int x = leader(a), y = leader(b);
        if (x == y) return x;
        if (-parent_or_size[x] < -parent_or_size[y]) std::swap(x, y);
        parent_or_size[x] += parent_or_size[y];
        parent_or_size[y] = x;
        return x;
    }

    bool same(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        return leader(a) == leader(b);
    }

    int leader(int a) {
        assert(0 <= a && a < _n);
        if (parent_or_size[a] < 0) return a;
        return parent_or_size[a] = leader(parent_or_size[a]);
    }

    int size(int a) {
        assert(0 <= a && a < _n);
        return -parent_or_size[leader(a)];
    }

    std::vector<std::vector<int>> groups() {
        std::vector<int> leader_buf(_n), group_size(_n);
        for (int i = 0; i < _n; i++) {
            leader_buf[i] = leader(i);
            group_size[leader_buf[i]]++;
        }
        std::vector<std::vector<int>> result(_n);
        for (int i = 0; i < _n; i++) {
            result[i].reserve(group_size[i]);
        }
        for (int i = 0; i < _n; i++) {
            result[leader_buf[i]].push_back(i);
        }
        result.erase(
            std::remove_if(result.begin(), result.end(),
                           [&](const std::vector<int>& v) { return v.empty(); }),
            result.end());
        return result;
    }

  private:
    int _n;
    // root node: -1 * component size
    // otherwise: parent
    std::vector<int> parent_or_size;
};

}  // namespace atcoder

const std::string First  = "Taro";
const std::string Second = "Hanako";

int main() {
    int n;
    std::cin >> n;
    bool m = (n * (n - 1) / 2) & 1;
    atcoder::dsu uf(n);
    for (int i = 0; i < n; ++i) {
        std::string s;
        std::cin >> s;
        for (int j = 0; j < n; ++j) {
            if (s[j] == 'Y') {
                uf.merge(i, j);
                if (i < j) m = not m;
            }
        }
    }

    // True <-> First, False <-> Second
    std::map<std::array<int, 2>, bool> dp;
    auto dfs = [&](auto dfs, std::array<int, 2> &cnt) -> bool {
        if (int sum = cnt[0] + cnt[1]; sum == 2) {
            bool p = m;
            if (cnt[0] == 0) p ^= (n - 1) & 1;
            return p;
        }

        if (auto it = dp.find(cnt); it != dp.end()) return it->second;

        for (int i = 0; i < 2; ++i) {
            if (cnt[i] == 0) continue;
            bool win = false;
            --cnt[i];
            for (int j = 0; j <= i; ++j) {
                if (cnt[j] == 0) continue;
                int k = i ^ j;
                --cnt[j], ++cnt[k];
                win = not dfs(dfs, cnt);
                ++cnt[j], --cnt[k];
                if (win) break;
            }
            ++cnt[i];
            if (win) return dp[cnt] = true;
        }
        return dp[cnt] = false;
    };

    std::array<int, 2> init { 0, 0 };
    for (auto group : uf.groups()) {
        ++init[group.size() & 1];
        m = not m;
    }

    std::cout << (dfs(dfs, init) ? First : Second) << std::endl;
    return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 5660
final_rank: 9

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define eps 1e-9
#define INF 2000000000              // 2e9
#define LLINF 2000000000000000000ll // 2e18 (llmax:9e18)
#define all(x) (x).begin(), (x).end()
#define sq(x) ((x) * (x))

#define rep(i, x) for (int i = 0; i < (int)(x); i++)
#define drep(i, x) for (int i = ((int)(x)-1); i >= 0; i--)

#define popcount __builtin_popcount
#define next __next
#define prev __prev

#ifndef LOCAL
#define dmp(...) ;
#else
#define dmp(...)                                                               \
  cerr << "[ " << #__VA_ARGS__ << " ] : " << dump_str(__VA_ARGS__) << endl
#endif

// ---------------- Utility ------------------

template <class T>
bool chmin(T &a, const T &b) {
  if (a <= b) return false;
  a = b;
  return true;
}

template <class T>
bool chmax(T &a, const T &b) {
  if (a >= b) return false;
  a = b;
  return true;
}

template <class T>
using MaxHeap = priority_queue<T>;

template <class T>
using MinHeap = priority_queue<T, vector<T>, greater<T>>;

template <class T>
vector<T> vect(int len, T elem) {
  return vector<T>(len, elem);
}

// ----------------- Input -------------------

template <class T, class U>
istream &operator>>(istream &is, pair<T, U> &p) {
  return is >> p.first >> p.second;
}

template <class T>
istream &operator>>(istream &is, vector<T> &vec) {
  for (int i = 0; i < vec.size(); i++) is >> vec[i];
  return is;
}

// ----------------- Output ------------------

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
  return os << p.first << ',' << p.second;
}

template <class T>
ostream &operator<<(ostream &os, const vector<T> &v) {
  for (const T &e : v) os << e << " ";
  return os;
}

template <class T>
ostream &operator<<(ostream &os, const deque<T> &d) {
  for (const T &e : d) os << e << " ";
  return os;
}

template <class T>
ostream &operator<<(ostream &os, const set<T> &s) {
  os << "{ ";
  for (const T &e : s) os << e << " ";
  return os << "}";
}

template <class T, class U>
ostream &operator<<(ostream &os, const map<T, U> &m) {
  os << "{ ";
  for (const auto &[key, val] : m) os << "( " << key << " -> " << val << " ) ";
  return os << "}";
}

template <class TupleTy, size_t... I>
void dump_tuple(ostream &os, const TupleTy t, std::index_sequence<I...>) {
  (..., (os << (I == 0 ? "" : ",") << std::get<I>(t)));
}

template <class... Args>
ostream &operator<<(ostream &os, const tuple<Args...> &t) {
  os << "(";
  dump_tuple(os, t, std::make_index_sequence<sizeof...(Args)>());
  return os << ")";
}

void dump_str_rec(ostringstream &) {}

template <class Head, class... Tail>
void dump_str_rec(ostringstream &oss, Head &&head, Tail &&... tail) {
  oss << ", " << head;
  dump_str_rec(oss, forward<Tail>(tail)...);
}

template <class T, class... U>
string dump_str(T &&arg, U &&... args) {
  ostringstream oss;
  oss << arg;
  dump_str_rec(oss, forward<U>(args)...);
  return oss.str();
}

// --------------- Fast I/O ------------------

void fastio() {
  cin.tie(0);
  ios::sync_with_stdio(0);
  cout << fixed << setprecision(20);
}

// ------------------ ACL --------------------

// #include <atcoder/modint>
// constexpr int MOD = 998244353;
// constexpr int MOD = 1000000007;
// using mint = atcoder::static_modint<MOD>;
// ostream &operator<<(ostream &os, const mint &v) {
//   return os << v.val();
// }

// ------------ End of template --------------

struct UnionFind {
  vector<int> par, rank, sz;
  UnionFind(int n) {
    par.assign(n, 0);
    rank.assign(n, 0);
    sz.assign(n, 1);
    for (int i = 0; i < n; i++) par[i] = i;
  }
  int find(const int x) { return (par[x] == x) ? x : (par[x] = find(par[x])); }
  void unite(int x, int y) {
    x = find(x);
    y = find(y);
    if (x == y) return;
    if (rank[x] < rank[y]) {
      par[x] = y;
      sz[y] += sz[x];
    } else {
      par[y] = x;
      sz[x] += sz[y];
      if (rank[x] == rank[y]) rank[x]++;
    }
  }
  bool same(const int x, const int y) { return find(x) == find(y); }
  int size(const int x) { return sz[find(x)]; }
  vector<vector<int>> components() {
    int n = par.size();
    vector<vector<int>> cs;
    vector<int> idx(n, -1);
    for (int i = 0; i < n; i++) {
      if (find(i) != i) continue;
      idx[i] = cs.size();
      cs.emplace_back();
    }
    for (int i = 0; i < n; i++) {
      int id = idx[find(i)];
      cs[id].push_back(i);
    }
    return cs;
  }
};

#define endl "\n"
using ll = long long;
using pii = pair<int, int>;

const bool multitest = false;

void solve() {
  int V;
  cin >> V;
  vector<string> g(V);
  cin >> g;
  int odd = 0, even = 0, parity = 0;
  UnionFind uf(V);
  for (int i = 0; i < V; i++) {
    for (int j = i + 1; j < V; j++) {
      if (g[i][j] == 'Y') {
        uf.unite(i, j);
        parity ^= 1;
      }
    }
  }
  for (int i = 0; i < V; i++) {
    if (uf.find(i) != i) continue;

    if (uf.size(i) % 2 == 1)
      odd++;
    else
      even++;

    parity ^= ((uf.size(i) / 2) & 1);
  }

  dmp(odd, even, parity);

  auto dp = vect(V + 1, vect(V + 1, vect(2, -1)));

  function<int(int, int, int)> rec = [&](int a, int b, int c) {
    if (dp[a][b][c] != -1) return dp[a][b][c];
    if (a + b == 2) { return dp[a][b][c] = c; }
    if (a >= 1 && b >= 1) {
      if (!rec(a - 1, b, !c)) return dp[a][b][c] = 1;
    }
    if (a >= 2) {
      if (!rec(a - 1, b, !c)) return dp[a][b][c] = 1;
    }
    if (b >= 2) {
      if (!rec(a + 1, b - 2, c)) return dp[a][b][c] = 1;
    }
    if (c == 1) {
      if (!rec(a, b, !c)) return dp[a][b][c] = 1;
    }
    return dp[a][b][c] = 0;
  };

  if (rec(even, odd, parity))
    cout << "Taro" << endl;
  else
    cout << "Hanako" << endl;
  return;
}

int main() {
  fastio();
  if (!multitest) {
    solve();
  } else {
    cerr << "[Warning] Multi testcase mode on" << endl;
    int t;
    cin >> t;
    while (t--) solve();
  }
  return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 59312
final_rank: 13

code_snippet: #include<bits/stdc++.h> 
using namespace std;
using ll=long long int;
//using Int=__int128;
#define ALL(A) A.begin(),A.end()
template<typename T1,typename T2> bool chmin(T1 &a,T2 b){if(a<=b)return 0; a=b; return 1;}
template<typename T1,typename T2> bool chmax(T1 &a,T2 b){if(a>=b)return 0; a=b; return 1;}
template<typename T> constexpr int bitUP(T x,int a){return (x>>a)&1;}
//→　↓　←　↑ 
int dh[4]={0,1,0,-1}, dw[4]={1,0,-1,0};
//右上から時計回り
//int dh[8]={-1,0,1,1,1,0,-1,-1}, dw[8]={1,1,1,0,-1,-1,-1,0};
//long double EPS = 1e-6;
//long double PI = acos(-1);
//const ll INF=(1LL<<62);
const int MAX=(1<<30);
constexpr ll MOD=1000000000+7;
//constexpr ll MOD=998244353;


inline void bin101(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout << fixed << setprecision(20);
}

using Graph=vector<vector<int>>;

template<typename T >
struct edge {
	int to;
	T cost;
	edge()=default;
	edge(int to, T cost) : to(to), cost(cost) {}

};
template<typename T>
using WeightGraph=vector<vector<edge<T>>>;

//要素数n 初期値x
template<typename T>
inline vector<T> vmake(size_t n,T x){
	return vector<T>(n,x);
}

//a,b,c,x data[a][b][c] 初期値x
template<typename... Args>
auto vmake(size_t n,Args... args){
	auto v=vmake(args...);
	return vector<decltype(v)>(n,move(v));
}
////////////////////////////
// マクロや型
////////////////////////////

using DD=long double;

const DD EPS=1e-9;
const DD INF=1e50;
const DD PI=acosl(-1.0);
#define EQ(a,b) (abs( (a) - (b) )<EPS) //a==b
#define LS(a,b) ( (a)+EPS<(b) ) //a<b
#define GR(a,b) ( (a)>(b)+EPS ) //a>b
#define LE(a,b) ( (a)<(b)+EPS ) //a<=b
#define GE(a,b) ( (a)>(b)-EPS ) //a>=b

#define X real()
#define Y imag()


//点
using Point=complex<DD>;
istream &operator>>(istream &is, Point &p) {
  DD x,y;
  is>>x>>y;
  p=Point(x,y);
  return is;
}

//点a→点bの線分
//aとbが等しい時、色々バグるから注意!
struct Segment{
    Point a,b;
    Segment()=default;
    Segment(Point a,Point b) :a(a),b(b){}
    Segment(DD ax,DD ay,DD bx,DD by):a(ax,ay),b(bx,by){}
    Segment(DD r,Point a) :a(a),b(a+polar((DD)1.0,r)){} 
};
using Line=Segment;

//円
struct Circle{
    Point p;
    DD r;
    Circle()=default;
    Circle(Point p,DD r):p(p),r(r){}
};

using Polygon=vector<Point>;

////////////////////////////
// 基本計算
////////////////////////////

//度→ラジアン
inline DD torad(const DD deg){return deg*PI/180;}
//ラジアン→度
inline DD todeg(const DD rad){return rad*180/PI;}
//内積 |a||b|cosθ
inline DD dot(const Point &a,const Point &b){return (conj(a)*b).X;}
//外積 |a||b|sinθ
inline DD cross(const Point &a,const Point &b){return (conj(a)*b).Y;}
//ベクトルpを反時計回りにtheta(ラジアン)回転
Point rotate(const Point &p,const DD theta){return p*Point(cos(theta),sin(theta));}

//余弦定理 cos(角度abc(ラジアン))を返す
//長さの対応に注意 ab:辺abの長さ
//verify:https://codeforces.com/gym/102056/problem/F
DD cosine_formula(const DD ab,const DD bc,const DD ca){
    return (ab*ab+bc*bc-ca*ca)/(2*ab*bc);
}

inline bool xy_sort(const Point &a,const Point &b){
    if(a.X+EPS<b.X) return true;
    if(EQ(a.X,b.X) && a.Y+EPS<b.Y) return true;
    return false;
}
inline bool yx_sort(const Point &a,const Point &b){
    if(a.Y+EPS<b.Y) return true;
    if(EQ(a.Y,b.Y) && a.X+EPS<b.X) return true;
    return false;
}
namespace std{
    inline bool operator<(const Point &a,const Point &b)noexcept{
        return xy_sort(a,b);
    }
}
//DDの比較関数
//map<DD,vector<pii>,DDcom>
struct DDcom{
    bool operator()(const DD a, const DD b) const noexcept {
        return a+EPS<b;
    }
};


////////////////////////////
// 平行や直交
////////////////////////////

bool isOrthogonal(const Point &a,const Point &b){
    return EQ(dot(a,b),0.0);
}
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_A
bool isOrthogonal(const Segment &s,const Segment &t){
    return EQ(dot(s.a-s.b,t.a-t.b),0.0);
}
bool isParallel(const Point &a,const Point &b){
    return EQ(cross(a,b),0.0);
}
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_A
bool isParallel(const Segment &s,const Segment &t){
    return EQ(cross(s.a-s.b,t.a-t.b),0);
}
//線分a→bに対して点cがどの位置にあるか
//反時計回り:1　時計回り:-1 直線上(a,b,c:-2 a,c,b:0 c,a,b:2)
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_C
//線分a→bの端点にcがあるなら0と判定されるはず
int ccw(const Point &a,Point b,Point c){
    if(EQ(abs(b-c),0) or EQ(abs(a-c),0)) return 0;
    b-=a;c-=a;
    if(cross(b,c)>EPS) return 1;
    if(cross(b,c)<-EPS) return -1;
    if(dot(b,c)<-EPS) return 2;
    if(LS(norm(b),norm(c))) return -2;
    return 0;
}

////////////////////////////
// 射影と反射
////////////////////////////

//射影
//直線lに点pから垂線を引いたときの交点
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_A
Point project(const Point &p,const Line &l){
    Point base=l.b-l.a;
    DD r=dot(p-l.a,base)/norm(base);
    return l.a+base*r;
}
//反射
//直線lに対して点pと対称な点
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_B
Point reflect(const Point &p,const Line &l){
    return p+(project(p,l)-p)*(DD)2.0;
}
//反射
//直線lに対して線分sと対称な線分(直線でも使える)
//verify:https://onlinejudge.u-aizu.ac.jp/solutions/problem/1171/review/6009447/bin101/C++17
Segment reflect(const Segment &s,const Line &l){
    return Segment(reflect(s.a,l) , reflect(s.b,l));
}

////////////////////////////
// 点との距離
////////////////////////////

//点と直線の距離
DD DistPL(const Point &p,const Line &l){
    return abs(cross(l.b-l.a,p-l.a))/abs(l.b-l.a);
}
//点と線分の距離
DD DistPS(const Point &p,const Segment &s){
    if( dot(s.b-s.a,p-s.a)<0.0 ) return abs(p-s.a);
    if( dot(s.a-s.b,p-s.b)<0.0 ) return abs(p-s.b);
    return DistPL(p,s);
}

////////////////////////////
// 線分と直線
////////////////////////////

//線分a-b,c-dは交差するか?
//接する場合も交差すると判定
//接する場合は交差しないとするなら<=を<に変換
bool intersect(const Point &a,const Point &b,const Point &c,const Point &d){
    return(ccw(a,b,c)*ccw(a,b,d)<=0 && ccw(c,d,a)*ccw(c,d,b)<=0);
}
//線分s,tは交差するか？
//接する場合も交差すると判定
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_B&lang=ja
//接する場合は交差しないとするならintersectの<=を<に変換
bool intersect(const Segment &s,const Segment &t){
    return intersect(s.a,s.b,t.a,t.b);
}
// 2直線の交点
// 線分の場合はintersectをしてから使う
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_C&lang=ja
Point crossPoint(const Line &s,const Line &t){
    Point base=t.b-t.a;
    DD d1=cross(base,t.a-s.a);
    DD d2=cross(base,s.b-s.a);
    if(EQ(d1,0) and EQ(d2,0)) return s.a; //同じ直線
    if(EQ(d2,0)) assert(false); //交点がない
    return s.a+d1/d2*(s.b-s.a);
}
//線分と線分の距離
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_D&lang=ja
DD Dist(const Segment &s,const Segment &t){
    if(intersect(s,t)) return 0.0;
    return min(min(DistPS(t.a,s),DistPS(t.b,s)),min(DistPS(s.a,t),DistPS(s.b,t)) );
}
bool issame(const Line &l,const Line &m){
    if (GR(abs(cross(m.a - l.a, l.b - l.a)),0) ) return false;
    if (GR(abs(cross(m.b - l.a, l.b - l.a)),0) ) return false;
    return true;
}

////////////////////////////
// 円
////////////////////////////
//直線lと円Cの交点
//l.aにより近い方がindexが小さい
//veirfy:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_D
vector<Point> crossPointLC(const Line &l,const Circle &c){
    vector<Point> ret;
    if(GR(DistPL(c.p,l),c.r)) return ret; //交点がないとき、空ベクトルを返す
    Point pr=project(c.p,l);
    Point e=(l.b-l.a)/(abs(l.b-l.a));
    DD base=sqrt(c.r*c.r-norm(pr-c.p));
    ret.push_back(pr-e*base);
    ret.push_back(pr+e*base);
    if(EQ(DistPL(c.p,l),c.r)) ret.pop_back(); //接するとき
    return ret;
}
//線分sと円cの交点
//交点がないときは空ベクトルを返す
//線分の端が交わる場合も交点とみなす
//s.aにより近い方がindexが小さい
vector<Point> crossPointSC(const Segment &s,const Circle &c){
    vector<Point> ret;
    if(DistPS(c.p,s)>c.r+EPS) return ret;
    auto koho=crossPointLC(s,c);
    for(auto p:koho){
        if(ccw(s.a,s.b,p)==0 or EQ(abs(p-s.a),0) or EQ(abs(p-s.b),0)) ret.push_back(p);
    }
    return ret;
}

//円aと円bの共通接線の数
//離れている:4 外接:3 交わる:2 内接:1 内包:0
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_A
int intersect(const Circle &a,const Circle &b){
    DD d=abs(a.p-b.p);
    if(GR(d,a.r+b.r)) return 4;
    if(EQ(d,a.r+b.r)) return 3;
    if(EQ(d,abs(a.r-b.r))) return 1;
    if(LS(d,abs(a.r-b.r))) return 0;
    return 2;
}

//円aと円bの交点
//交点がないときは空ベクトルを返す
//
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_E
vector<Point> crossPoint(const Circle &a,const Circle &b){
    vector<Point> ret;
    if(GR(abs(a.p-b.p),a.r+b.r)) return ret; //離れている
    DD d=abs(a.p-b.p);
    //cerr<<(a.r*a.r+d*d-b.r*b.r)/(2*a.r*d)<<endl;
    DD s=acosl((a.r*a.r+d*d-b.r*b.r)/(2*a.r*d)); //0~π
    DD t=arg(b.p-a.p);
    if(EQ(a.r+b.r,d)) ret.push_back(a.p+polar(a.r,t));
    else ret.push_back(a.p+polar(a.r,t+s)),ret.push_back(a.p+polar(a.r,t-s));
    return ret;
}

//点pから円cに接線を引いたときの接点
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_F&lang=ja
vector<Point> TanLine(const Point &p,const Circle &c){
    vector<Point> ret;
    DD d=abs(p-c.p);
    if(LS(d,c.r)) return ret; //pがcの内部にある場合
    if(EQ(d,c.r)){
        ret.push_back(p);
        return ret; //円の線上にある場合
    } 
    return crossPoint(c,Circle(p,sqrt(d*d-c.r*c.r)));
}

//円a,bの共通接線
//https://www.slideshare.net/kujira16/ss-35861169
//Line.aは円aと接線の交点
//(接線と円bの交点)と(接線と円aの交点)が違う場合はLine.bは(接線と円bの交点)
//一致する場合は円aの中心からLine.aに向かうベクトルを反時計回りに90度回転したものを
//Line.aに足したもの
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_G
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2201
vector<Line> TanLine(const Circle &a,const Circle &b){
    DD d=abs(a.p-b.p);
    vector<Line> ret;
    //外接線
    if(intersect(a,b)>=2){
        if(EQ(a.r,b.r)){
            Point up=polar(a.r,arg(b.p-a.p)+PI/2);
            ret.emplace_back(a.p+up,b.p+up);
            ret.emplace_back(a.p-up,b.p-up);
        }else{
            Point q=(a.p*b.r-b.p*a.r)/(b.r-a.r);
            auto as=TanLine(q,a);
            auto bs=TanLine(q,b);
            assert(as.size()==2 and bs.size()==2);
            for(int i=0;i<2;i++){
                ret.emplace_back(as[i],bs[i]);
            }
        }
    }else if(intersect(a,b)==1){ //内接する場合
        Point dir;
        if(a.r>b.r) dir=polar(a.r,arg(b.p-a.p));
        else dir=polar(a.r,arg(a.p-b.p));
        ret.emplace_back(a.p+dir,a.p+dir+rotate(dir,PI/2));
    }
    //内接線
    if(GR(abs(a.p-b.p),a.r+b.r)){ //円が離れている場合
        Point q=a.p+(b.p-a.p)*a.r/(a.r+b.r);
        auto as=TanLine(q,a);
        auto bs=TanLine(q,b);
        assert(as.size()==2 and bs.size()==2);
        for(int i=0;i<2;i++){
            ret.emplace_back(as[i],bs[i]);
        }
    }else if(EQ(d,a.r+b.r)){ //円が接している場合
        Point dir=polar(a.r,arg(b.p-a.p));
        ret.emplace_back(a.p+dir,a.p+dir+rotate(dir,PI/2));
    }
    return ret;
}
//２つの円の共通部分の面積
//参考: https://tjkendev.github.io/procon-library/python/geometry/circles_intersection_area.html
//verify: https://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=6005736#1
DD Area(const Circle &c1,const Circle &c2){
    DD cd=abs(c1.p-c2.p);
    if(LE(c1.r+c2.r,cd)) return 0; //円が離れている

    if(LE(cd,abs(c1.r-c2.r))) //片方の円が他方を包んでいる
        return min(c1.r,c2.r)*min(c1.r,c2.r)*PI;

    DD theta1=acosl(cosine_formula(c1.r,cd,c2.r));
    DD theta2=acosl(cosine_formula(c2.r,cd,c1.r));

    return c1.r*c1.r*theta1+c2.r*c2.r*theta2-c1.r*cd*sin(theta1);
}


////////////////////////////
// 三角形
////////////////////////////

//ヘロンの公式　三角形の面積を返す
//三角形が成立するか(平行ではないか)を忘れずに
DD Heron(const DD ad,const DD bd,const DD cd){
    DD s=(ad+bd+cd)/2;
    return sqrt(s*(s-ad)*(s-bd)*(s-cd));
}
//三角形の外接円
//isParallel()を使って三角形が成立するか(平行ではないか)を忘れずに
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_C
Circle CircumscribedCircle(const Point &a,const Point &b,const Point &c){
    Point bc=(b+c)/(DD)2.0;
    Line aa(bc,bc+rotate(c-b,PI/2));
    Point ab=(a+b)/(DD)2.0;
    Line cc(ab,ab+rotate(b-a,PI/2));
    Point p=crossPoint(aa,cc);
    return Circle(p,abs(a-p));
}
//三角形の内接円
//isParallel()を使って三角形が成立するか(平行ではないか)を忘れずに
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_B
Circle InCircle(const Point &a,const Point &b,const Point &c){
    Line aa(a,a+polar((DD)1.0,(arg(b-a)+arg(c-a))/(DD)2.0));
    Line bb(b,b+polar((DD)1.0,(arg(a-b)+arg(c-b))/(DD)2.0));
    Point p=crossPoint(aa,bb);
    return Circle(p,DistPL(p,Line(a,b)));
}

////////////////////////////
// 多角形
////////////////////////////

//点pが多角形gに対してどの位置にあるか
//中:2 辺上:1 外:0
//凸多角形である必要ない
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_3_C
int contains(const Point &p,const vector<Point> &g){
    int n=(int)g.size();
    int x=false;
    for(int i=0;i<n;i++){
        Point a=g[i]-p,b=g[(i+1)%n]-p;
        if(EQ(cross(a,b),0) && dot(a,b)<EPS) return 1;
        if(a.Y>b.Y) swap(a,b);
        if(a.Y<EPS && EPS<b.Y && cross(a,b)>EPS) x=!x;
    }
    return (x?2:0);
}
//凸性判定
//多角形gは凸か？
//gは反時計回りでも時計回りでもいい
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_3_B
bool isConvex(const vector<Point> &g){
    int n=(int)g.size();
    if(n<=3) return true;
    int flag=0;
    int t;
    for(int i=0;i<n;i++){
        Point a(g[(i+1)%n]-g[i]),b(g[(i+2)%n]-g[i]);
        if(cross(a,b)>EPS) t=1;
        else if(cross(a,b)<-EPS) t=-1;
        else continue;
        if(flag==-t) return false;
        flag=t;
    }
    return true;
}

//凸包　アンドリューのアルゴリズム
//O(NlogN)
//反時計回りの多角形を返す
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_4_A
vector<Point> ConvexHull(vector<Point> s,const bool on_edge){
    int j;
    if(on_edge) j=-1;
    else j=1;
    int sz=(int)s.size();
    if(sz<3) return s;
    sort(s.begin(),s.end(),yx_sort);

    int n=0;
    vector<Point> res(2*sz);
    for(int i=0;i<sz;i++){
        while(n>=2 && cross(res[n-1]-res[n-2],s[i]-res[n-2])<EPS*j){
            n--;
        }
        res[n]=s[i];
        n++;
    }
    int t=n+1;
    for(int i=sz-2;i>=0;i--){
        while(n>=t && cross(res[n-1]-res[n-2],s[i]-res[n-2])<EPS*j){
            n--;
        }
        res[n]=s[i];
        n++;
    }
    res.resize(n-1);
    return res;
}

//多角形g(凸でなくてもいい)の符号付き面積
//反時計回りの多角形なら正の値を返す　時計回りなら負
//O(N)
//https://imagingsolution.net/math/calc_n_point_area/
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_3_A
DD Area(const vector<Point> &g){
    DD ret=0.0;
    int n=(int)g.size();
    for(int i=0;i<n;i++){
        ret+=cross(g[i],g[(i+1)%n]);
    }
    return ret/2.0;
}

//凸多角形gを直線lで切断
//直線の左側(向きを考慮)にできる凸多角形を返す
//多角形gが反時計回りならば、反時計回りで返す(時計回りなら時計回り)
//直線上の頂点を含む線分の交点は含めてない
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_4_C
vector<Point> ConvexCut(const vector<Point> &g,const Line &l){
    vector<Point> ret;
    int gz=(int)g.size();
    for(int i=0;i<gz;i++){
        Point now=g[i],next=g[(i+1)%gz];
        if(ccw(l.a,l.b,now)!=-1) ret.push_back(now);
        if(EQ(DistPL(now,l),0) or EQ(DistPL(next,l),0)) continue;
        if(ccw(l.a,l.b,now)*ccw(l.a,l.b,next)<0){
            ret.push_back(crossPoint(Line(now,next),l));
        }
    }
    return ret;
}
//部品
inline DD calc(const Point &a,const Point &b,const DD &r,const bool triangle){
    if(triangle) return cross(a,b);
    else return r*r*arg(b/a);
}
//部品
DD calcArea(const DD &r,const Point &a,const Point &b){
    if(EQ(abs(a-b),0)) return 0;
    bool ina=(abs(a)<r+EPS);
    bool inb=(abs(b)<r+EPS);
    if(ina && inb) return cross(a,b);
    auto cr=crossPointSC(Segment(a,b),Circle(Point(0,0),r));
    if(cr.empty()) return calc(a,b,r,false);
    auto s=cr[0],t=cr.back();
    return calc(s,t,r,true)+calc(a,s,r,ina)+calc(t,b,r,inb);
}
//円と多角形の共通部分の面積
//多角形が反時計回りならば正の値を返す
//凸多角形でなくても大丈夫
//http://drken1215.hatenablog.com/entry/2020/02/02/091000
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_H&lang=ja
DD Area(const Circle &c,const vector<Point> &g){
    DD ret=0.0;
    int gz=g.size();
    if(gz<=2) return ret;
    for(int i=0;i<gz;i++){
        Point a=g[i]-c.p,b=g[(i+1)%gz]-c.p;
        ret+=calcArea(c.r,g[i]-c.p,g[(i+1)%gz]-c.p);
    }
    return ret/2.0;
}

//点と多角形の距離
//点が多角形の中にあるなら0
DD Dist(const Point &a,const vector<Point> &b){
	if(b.size()==1) return abs(a-b[0]);
	if(contains(a,b)>=1) return 0.0L;
	DD ret=INF;
	for(int i=0;i<b.size();i++){
		chmin(ret,DistPS(a,Segment(b[i],b[(i+1)%b.size()])));
	}
	return ret;
}

//多角形と多角形の距離
//どちらかが内側に入っていたら0
//全ての線分の組み合わせの距離を求めて、その最小が答え
//O(size(a)*size(b))
//sizeが1の時は点との距離になる
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2827
DD Dist(const vector<Point> &a,const vector<Point> &b){
	DD ret=INF;
	if(a.size()==1) return Dist(a[0],b);
	if(b.size()==1) return Dist(b[0],a);

	if(contains(a[0],b)>=1) return 0.0L;
	if(contains(b[0],a)>=1) return 0.0L;

	for(int i=0;i<a.size();i++){
		for(int j=0;j<b.size();j++){
			Segment sa(a[i],a[(i+1)%a.size()]);
			Segment sb(b[j],b[(j+1)%b.size()]);
			chmin(ret,Dist(sa,sb));
		}
	}
	return ret;
}

////////////////////////////
// 最近(遠)点間距離
////////////////////////////
//部品
DD RecClosetPair(const vector<Point>::iterator it,const int n){
    if(n<=1) return INF;
    int m=n/2;
    DD x=it[m].X;
    DD d=min(RecClosetPair(it,m),RecClosetPair(it+m,n-m));
    inplace_merge(it,it+m,it+n,yx_sort);
    vector<Point> v;
    for(int i=0;i<n;i++){
        if(abs(it[i].X-x)>=d) continue;
        for(int j=0;j<v.size();j++){
            DD dy=it[i].Y-v[(int)v.size()-1-j].Y;
            if(dy>=d) break;
            DD dx=it[i].X-v[(int)v.size()-1-j].X;
            d=min(d,sqrt(dx*dx+dy*dy));
        }
        v.push_back(it[i]);
    }
    return d;
}
//最近点対の距離
//点が1つのときINFを返す
//O(NlogN)
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_5_A
DD ClosetPair(vector<Point> g){
    sort(g.begin(),g.end(),xy_sort);
    return RecClosetPair(g.begin(),g.size());
}

//最遠頂点間
//verify:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_4_B
DD Diameter(vector<Point> g){
    g=ConvexHull(g,1);
    int gz=g.size();
    int m=0,M=0;
    for(int i=1;i<gz;i++){
        if(g[i].Y<g[m].Y) m=i;
        if(g[i].Y>g[M].Y) M=i;
    }
    DD ret=0;
    int sm=m,sM=M;
    while(m!=sM || M!=sm){
        ret=max(ret,norm(g[m]-g[M]));
        if(cross(g[(m+1)%gz]-g[m],g[(M+1)%gz]-g[M])<0) m=(m+1)%gz;
        else M=(M+1)%gz;
    }
    return sqrt(ret);
}
//動的凸包
//辺上にある点は含めない
//verify:https://atcoder.jp/contests/geocon2013/tasks/geocon2013_c
struct DynamicConvex{
	set<Point> up,down;
	DynamicConvex(){}
 
	bool isContain(set<Point> &pol,Point p){
		if(pol.size()==0) return false;
		if(pol.size()==1){
			Point q=*begin(pol);
			return EQ(q.X,p.X) and GE(q.Y,p.Y);
		}
		assert(pol.size()>=2);
        auto left=begin(pol);
        auto right=(--end(pol));
        if(LS(p.X,left->X) or GR(p.X,right->X)) return false;
        
        
        auto q1=pol.upper_bound(Point(p.X,INF));
        if(q1==end(pol)){
            return GE(right->Y,p.Y);
        }
        auto q2=q1--; //q1が範囲外になるのは最初に弾いてる
        return GE(cross(p-*q1,*q2-*q1),0.0);
	}
    bool isContain(Point p){
        if(not isContain(up,p)) return false;
        if(not isContain(down,Point(p.X,-p.Y))) return false;
        return true;
    }
 
	void add(set<Point> &pol,Point p){
		if(pol.size()==0){
			pol.insert(p);
			return;
		}
		if(pol.size()==1){
            Point q=*begin(pol);
			if(EQ(q.X,p.X)){
                pol.clear();
                pol.insert(max(p,q));
			}else{
				pol.insert(p);
			}
            return;
		}
        assert(pol.size()>=2);
        if(isContain(pol,p)) return;
        //left
        auto q1=pol.upper_bound(Point(p.X,INF));
        if(q1!=begin(pol)){
            q1--;
            while(pol.size()>=1 and q1!=begin(pol)){
                auto q2=q1--;
                if(GR(cross(*q2-p,*q1-p),0)) break;
                pol.erase(q2);
            }
        }
        //right
        q1=pol.lower_bound(Point(p.X,-INF));
        if(q1!=end(pol)){
            while(pol.size()>1 and q1!=--end(pol)){
                auto q2=q1++;
                if(GR(cross(*q1-p,*q2-p),0.0)) break;
                pol.erase(q2);
            }
        }
        pol.insert(p);
	}
 
	void add(Point p){
		add(up,p);
		add(down,Point(p.X,-p.Y)); //upと同じように処理をしたいから
	}
};

//垂直二等分線
//p-↑-q こんなかんじ
//voronoiで使う
Line bisector(const Point &p, const Point &q) {
    Point c=(p+q)/DD(2.0);
    Point v=rotate(q-p,PI/2);
    v/=abs(v);
    return Line(c-v,c+v);
}

//ボロノイ図
//pol:全体 ps:点の集合 id:中心となる点の添字 pp:中心となる点
//計算量: pol.size()*ps.size()
//verify: https://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=6005648#1
vector<Point> voronoi(Polygon pol,const vector<Point> &ps,int id=-1,const Point pp=Point()){
    Point p;
    if(id!=-1) p=ps[id];
    else p=pp;
    for(size_t i=0;i<ps.size();i++){
        if(i==id) continue;
        Line l=bisector(p,ps[i]);
        pol=ConvexCut(pol,l);
    }
    return pol;
}
struct UnionFind{
    int group; //集合の数
    vector<int> par;
    UnionFind(int N) : par(N,-1),group(N) {}
    
    //rootを探す
    int find(int x){
        if(par[x]<0) return x;
        else return par[x]=find(par[x]);
    }
    //集合の要素数
    int usize(int x) {return -par[find(x)];}
    //xとyが繋がっていたらfalseを返す
    bool unite(int x,int y){
        x=find(x);
        y=find(y);
        if(x==y) return false;
        group--;
        if(usize(x)<usize(y)) swap(x,y);
        par[x]+=par[y];
        par[y]=x;
        return true;
    }

    bool same(int x,int y) {return find(x)==find(y);}

};
vector<vector<vector<int>>> dp;

bool dfs(int even,int odd,int nokori){
    if(dp[even][odd][nokori]) return dp[even][odd][nokori]==1;
    if(even+odd==2){
        if(nokori%2==1) return true;
        return false;
    }
    bool win=false;
    //偶数同士
    if(even>=2) win|=(not dfs(even-1,odd,1-nokori));
    //偶数と奇数
    if(even and odd) win|=(not dfs(even-1,odd,1-nokori));
    //奇数と奇数
    if(odd>=2) win|=(not dfs(even+1,odd-2,nokori));
    int ret=-1;
    if(win) ret=1;
    if(even and odd) win|=(not dfs(even-1,odd,1-nokori));
    return (dp[even][odd][nokori]=ret)==1;
}

void solve(){
    int N; cin>>N;
    auto C=vmake(N,N,'a');
    for(auto &v:C){
        for(auto &c:v) cin>>c;
    }
    UnionFind uni(N);
    int nokori=0;
    for(int i=0;i<N;i++){
        for(int j=i+1;j<N;j++){
            if(C[i][j]=='Y') uni.unite(i,j),nokori--; 
        }
    }
    int odd=0,even=0;
    for(int i=0;i<N;i++){
        if(uni.find(i)!=i) continue;
        if(uni.usize(i)%2==0) even++;
        else odd++;
        nokori+=uni.usize(i)*(uni.usize(i)-1)/2;
    }
    nokori%=2;
    dp=vmake(odd+even+1,even+odd+1,2,0);
    if(dfs(even,odd,nokori)) cout<<"Taro"<<endl;
    else cout<<"Hanako"<<endl;
}

int main(){
    bin101();
    int T=1;
    //cin>>T;
    while(T--) solve();
}
accuracy: 1
time_ms: 50
memory_kb: 58876
final_rank: 14

code_snippet: #include <bits/stdc++.h>
using namespace std;

struct UF {
    vector<int> data;
    UF(int n) : data(n, -1) {}
    int root(int k) {
        if (data[k] < 0)
            return k;
        return data[k] = root(data[k]);
    }
    bool unite(int x, int y) {
        x = root(x), y = root(y);
        if (x == y)
            return false;
        if (data[x] > data[y])
            swap(x, y);
        data[x] += data[y];
        data[y] = x;
        return true;
    }
    bool same(int x, int y) {
        return root(x) == root(y);
    }
    int size(int k) { return -data[root(k)]; }
};

int V;
vector<string> A;
int Sum = 0;
int Ne = 0, even = 0, odd = 0, groups = 0;

vector<vector<bool>> seen, memo;

bool f(int e, int o) {
    auto&& ret = memo[e][o];
    if (seen[e][o])
        return ret;
    seen[e][o] = true;
    if (e == 2 && o == 0) {
        ret = (Sum + Ne + groups) % 2 == 1;
    } else if (e == 0 && o == 2) {
        ret = (Sum + Ne + groups) % 2 == 0;
    } else {
        if ((e >= 2 || (e >= 1 && o >= 1)) && !f(e - 1, o))
            ret = true;
        if (o >= 2 && !f(e + 1, o - 2))
            ret = true;
    }
    return ret;
}

int main() {
    cin >> V;
    A.resize(V);
    for (auto&& a : A) {
        cin >> a;
    }

    UF uf(V);
    for (int i = 0; i < V; i++) {
        for (int j = 0; j < i; j++) {
            if (A[i][j] == 'Y') {
                Ne++;
                uf.unite(i, j);
            }
        }
    }
    map<int, int> mp;
    for (int i = 0; i < V; i++) {
        mp[uf.root(i)] = uf.size(i);
    }
    for (auto&& [key, n] : mp) {
        (n % 2 == 0 ? even : odd)++;
    }
    groups = even + odd;

    Sum = V * (V - 1) / 2;

    bool maine;
    if (V % 2 == 1) {
        maine = (Sum + Ne) % 2;
    } else {
        seen.assign(even + odd + 1, vector<bool>(odd + 1, false));
        memo.assign(even + odd + 1, vector<bool>(odd + 1, false));
        maine = f(even, odd);
    }
    cout << (maine ? "Taro" : "Hanako") << endl;
}
accuracy: 1
time_ms: 10
memory_kb: 5596
final_rank: 1

code_snippet: #include <bits/stdc++.h>
using namespace std;
int main(){
  int V;
  cin >> V;
  vector<string> E(V);
  for (int i = 0; i < V; i++){
    cin >> E[i];
  }
  int M = 0;
  for (int i = 0; i < V; i++){
    for (int j = i + 1; j < V; j++){
      if (E[i][j] == 'Y'){
        M++;
      }
    }
  }
  if (V % 4 == 1){
    if (M % 2 == 0){
      cout << "Hanako" << endl;
    } else {
      cout << "Taro" << endl;
    }
  } else if (V % 4 == 3){
    if (M % 2 == 0){
      cout << "Taro" << endl;
    } else {
      cout << "Hanako" << endl;
    }
  } else {
    vector<int> cnt(2, 0);
    vector<bool> used(V, false);
    int sum = 0;
    for (int i = 0; i < V; i++){
      if (!used[i]){
        used[i] = true;
        int sz = 0;
        queue<int> Q;
        Q.push(i);
        while (!Q.empty()){
          int v = Q.front();
          Q.pop();
          sz++;
          for (int w = 0; w < V; w++){
            if (E[v][w] == 'Y' && !used[w]){
              used[w] = true;
              Q.push(w);
            }
          }
        }
        cnt[sz % 2]++;
        sum += sz * (sz - 1) / 2;
      }
    }
    sum -= M;
    if ((V % 4 == 0) ^ (M % 2 == 0)){
      if (cnt[0] > 0){
        cout << "Taro" << endl;
      } else if (cnt[1] == 2){
        cout << "Hanako" << endl;
      } else if (cnt[1] == 4 && sum % 2 == 0){
        cout << "Hanako" << endl;
      } else {
        cout << "Taro" << endl;
      }
    } else {
      if (cnt[0] >= 2){
        cout << "Hanako" << endl;
      } else if (cnt[1] == 2){
        cout << "Taro" << endl;
      } else if (cnt[1] == 4 && sum % 2 == 1){
        cout << "Taro" << endl;
      } else {
        cout << "Hanako" << endl;
      }
    }
  }
}
accuracy: 1
time_ms: 20
memory_kb: 3820
final_rank: 7

code_snippet: #include <bits/stdc++.h>
using namespace std;
int main(){
  int V;
  cin >> V;
  vector<string> E(V);
  for (int i = 0; i < V; i++){
    cin >> E[i];
  }
  int M = 0;
  for (int i = 0; i < V; i++){
    for (int j = i + 1; j < V; j++){
      if (E[i][j] == 'Y'){
        M++;
      }
    }
  }
  if (V % 4 == 1){
    if (M % 2 == 0){
      cout << "Hanako" << endl;
    } else {
      cout << "Taro" << endl;
    }
  } else if (V % 4 == 3){
    if (M % 2 == 0){
      cout << "Taro" << endl;
    } else {
      cout << "Hanako" << endl;
    }
  } else {
    vector<int> cnt(2, 0);
    vector<bool> used(V, false);
    for (int i = 0; i < V; i++){
      if (!used[i]){
        used[i] = true;
        int sz = 0;
        queue<int> Q;
        Q.push(i);
        while (!Q.empty()){
          int v = Q.front();
          Q.pop();
          sz++;
          for (int w = 0; w < V; w++){
            if (E[v][w] == 'Y' && !used[w]){
              used[w] = true;
              Q.push(w);
            }
          }
        }
        cnt[sz % 2]++;
      }
    }
    if ((V % 4 == 0) ^ (M % 2 == 0)){
      if (cnt[0] == 0){
        cout << "Hanako" << endl;
      } else {
        cout << "Taro" << endl;
      }
    } else {
      if (cnt[0] == 1 || (cnt[0] == 0 && cnt[1] == 2)){
        cout << "Taro" << endl;
      } else {
        cout << "Hanako" << endl;
      }
    }
  }
}
accuracy: 1
time_ms: 20
memory_kb: 3804
final_rank: 6

code_snippet: #include <bits/stdc++.h>
using namespace std;
int main(){
  int V;
  cin >> V;
  vector<string> E(V);
  for (int i = 0; i < V; i++){
    cin >> E[i];
  }
  int M = 0;
  for (int i = 0; i < V; i++){
    for (int j = i + 1; j < V; j++){
      if (E[i][j] == 'Y'){
        M++;
      }
    }
  }
  if (V % 4 == 1){
    if (M % 2 == 0){
      cout << "Hanako" << endl;
    } else {
      cout << "Taro" << endl;
    }
  } else if (V % 4 == 3){
    if (M % 2 == 0){
      cout << "Taro" << endl;
    } else {
      cout << "Hanako" << endl;
    }
  } else {
    int cnt = 0;
    vector<bool> used(V, false);
    for (int i = 0; i < V; i++){
      if (!used[i]){
        used[i] = true;
        int sz = 0;
        queue<int> Q;
        Q.push(i);
        while (!Q.empty()){
          int v = Q.front();
          Q.pop();
          sz++;
          for (int w = 0; w < V; w++){
            if (E[v][w] == 'Y' && !used[w]){
              used[w] = true;
              Q.push(w);
            }
          }
        }
        if (sz % 2 == 0){
          cnt++;
        }
      }
    }
    if ((V % 4 == 0) ^ (M % 2 == 0)){
      if (cnt == 0){
        cout << "Hanako" << endl;
      } else {
        cout << "Taro" << endl;
      }
    } else {
      if (cnt <= 1){
        cout << "Taro" << endl;
      } else {
        cout << "Hanako" << endl;
      }
    }
  }
}
accuracy: 0.8571428571428571
time_ms: 20
memory_kb: 3728
final_rank: 20

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for(int i=0;i<n;i++)
#define repn(i, n) for(int i=1;i<=n;i++)
#define pb push_back
#define vc vector
int w[6][4][2];
int rec(int o, int e, int z){
	int &r = w[o][e][z];
	if(r != -1) return r;
	if(o+e == 2) return r=z^1;
	if(o >= 2 && rec(o-2, e+1, z)) return r=0;
	if(e >= 2 && rec(o, e-1, z^1)) return r=0;
	if(o && e && rec(o, e-1, z^1)) return r=0;
	return r=1;
}
int main(){
	memset(w, -1, sizeof(w));
	rep(i, 6) rep(j, 4) if(i+j > 1) rep(k, 2) rec(i, j, k);
	int n; cin >> n;
	vc<bool>vis(n, 0);
	vc<string>s(n);
	int o = 0, e = 0, p = 0;
	rep(i, n){
		cin >> s[i];
		rep(j, n) p += s[i][j] == 'Y';
	}
	p /= 2;
	p &= 1;
	rep(i, n){
		if(vis[i]) continue;
		queue<int>q; q.push(i);
		int c = 0;
		while(!q.empty()){
			int Q = q.front(); q.pop();
			if(vis[Q]) continue;
			vis[Q] = 1; c ++;
			rep(j, n){
				if(s[Q][j] == 'Y' && !vis[j]) q.push(j);
			}
		}
		if(c&1) o++; else e++;
		if(c*(c-1)%4 == 2) p ^= 1;
	}
	while(o >= 6) o -= 4;
	while(e >= 4) e --;
	cout << (w[o][e][p]?"Hanako":"Taro") << '\n';
}
accuracy: 1
time_ms: 20
memory_kb: 3964
final_rank: 8