Problem:
Problem B: Blame Game
Alice and Bob are in a factional dispute. Recently a big serious problem
arised in a project both Alice and Bob had been working for. This problem was
caused by lots of faults of Alice's and Bob's sides; those faults are closely
related.
Alice and Bob started to blame each other. First, Alice claimed it was
caused by Bob's fault. Then Bob insisted his fault was led by Alice's fault.
Soon after, Alice said that her fault should not have happened without Bob's
another fault. So on so forth. It was terrible. It was totally a blame game.
Still, they had their pride. They would not use the same fault more than once in
their claims.
All right, let's see the situation in terms of a game.
Alice and Bob have a number of faults. Some pairs of Alice and Bob faults
have direct relationship between them. This relationship is bidirectional; if a
fault X is led by another fault Y, they can say either "X was due to Y." or
"even with X, the problem could be avoided without Y." Note that not both, since
they never pick up the same fault in their claims.
Alice and Bob take their turns alternatively. Alice takes the first turn
by claiming any of Bob's faults. Then Bob makes his claim with Alice's fault
directly related to the claimed fault. Afterward, in each turn, one picks up
another's fault directly related to the fault claimed in the previous turn. If
he/she has no faults that have not been claimed, then he/she loses this game.
By the way, you have been working both under Alice and Bob. You know all
the faults and relationships. Your task is to write a program to find out which
would win this game, under the assumption that they always take their optimal
strategies. If you could choose the winning side, you would not have to take the
responsibility for the arisen problem.
Input
Each input contains one test case. The first line of the input contains
two integers
N
and
M
(0 <=
N
,
M
<=
500), which denote the numbers of Alice's and Bob's faults respectively. Alice's
faults are numbered from 1 to
N
; so are Bob's from 1 to
M
.
Then
N
lines follow to describe the relationships among the faults.
The
i
-th line begins with a non-negative integer
K
i
(0 <=
K
i
<=
M
). It is then followed by
K
i
positive integers, where the
j
-th number
b
i,j
(1 <=
b
i,j
<=
M
) indicates there is a direct
relationship between the
i
-th Alice's fault and the
b
i,j
-th
Bob's fault. It is guaranteed that
b
i,j
!=
b
i,j'
for all
i
,
j
,
j'
such that 1 <=
i
<=
N
and 1 <=
j
<
j'
<=
K
i
.
Output
Print either "Alice" or "Bob" to indicate the winner of the blame game.
Sample Input 1
1 1
1 1
Output for the Sample Input 1
Bob
Sample Input 2
3 3
3 1 2 3
1 3
1 3
Output for the Sample Input 2
Alice

Submitted solutions:

code_snippet: #include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <map>
#include <stack>
#define ll long long
using namespace std;


const int maxm = 550 * 550;
const int maxn = 1100;
struct EE
{
    int to,ne;
}e[2 * maxm];
int head[maxn],L,matc[maxn],N;
bool use[maxn];
void adde(int u,int v)
{
    e[L].to = v;
    e[L].ne = head[u];
    head[u] = L++;
}
bool check(int u)
{
    if (use[u]) return false;
    use[u] = true;
    for (int i = head[u]; i != -1; i = e[i].ne) {
        int v = e[i].to;
        if (matc[v] == u) continue;
        if (!use[v]) {
            use[v] = true;
            if (matc[v] == -1 || check(matc[v])) {
                matc[v] = u;
                matc[u] = v;
                return true;
            }
        }
    }
    return false;
}
int match()
{
    int ret = 0;
    memset(matc,-1,sizeof(matc));
    for (int u = 0; u < N; u++) {
        if (matc[u] != -1) continue;
        memset(use,0,sizeof(use));
        if (check(u))
            ret++;
    }
    return ret;
}
int n,m;
int main()
{
    scanf("%d%d",&n,&m);
    L = 0;
    for (int i = 0; i < n + m; i++)
        head[i] = -1;
    N = n + m;
    for (int i = 0; i < n; i++) {
        int k,a;
        scanf("%d",&k);
        for (int j = 0; j < k; j++) {
            scanf("%d",&a);
            adde(i,n + a);
            adde(n + a, i);
        }
    }
    if (match() == m)
        printf("Bob\n");
    else
        printf("Alice\n");
}
accuracy: 1
time_ms: 40
memory_kb: 7492
final_rank: 11

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <set>
#include <iomanip>
using namespace std;
typedef long long ll;
#define rep(i, n) for(int i = 0; i < (n); i++)

template<class T>
using vi = vector<T>;

template<class T>
using vii = vector<vi<T>>;

template<class T>
using viii = vector<vii<T>>;


//Dinic, 計算量(O(V^2 E))
struct Dinic {
private:
    const long long inf = 1e18;
    vector<int> dist; //sからの距離
    vector<int> iter; //どこまで調べたか
public:
    struct edge {
        int to, rev; long long cap;
        edge(int t = 0, int r = 0, long long c = 0LL) : to(t), rev(r), cap(c) {}
    };

    int n;
    vector<vector<edge>> to;

    Dinic(int n_ = 1) : n(n_), to(n_), dist(n_), iter(n_) {}

    void addedge(int u, int v, long long cap) {//u->vにcapの容量
        int su = (int)to[u].size(), sv = (int)to[v].size();
        to[u].push_back({ v, sv, cap });
        to[v].push_back({ u, su, 0 });
    }

    long long dfs(int v, int t, long long f) { //t: 終点, f: flow
        if (v == t) return f;
        int sv = (int)to[v].size();
        for (int& i = iter[v]; i < sv; i++) {
            edge& nv = to[v][i];
            if (dist[nv.to] <= dist[v] || nv.cap == 0) continue; //here
            long long nf = dfs(nv.to, t, min(f, nv.cap));
            if (nf > 0) {
                nv.cap -= nf;
                to[nv.to][nv.rev].cap += nf;
                return nf;
            }
        }
        return 0;
    }

    void bfs(int s) {
        dist.assign(n, -1);
        queue<int> q;
        q.push(s);
        dist[s] = 0;
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            for (auto nv : to[v]) {
                if (nv.cap > 0 && dist[nv.to] < 0) {
                    dist[nv.to] = dist[v] + 1;
                    q.push(nv.to);
                }
            }
        }
    }

    long long maxflow(int s, int t) {//s:始点, t:終点
        long long res = 0, flow = 0;
        while (true) {
            bfs(s);
            if (dist[t] < 0) break;

            iter.assign(n, 0);
            while (flow = dfs(s, t, inf)) res += flow;
        };
        return res;
    }
};

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

    int S = n + m, T = n + m + 1;
    Dinic mf(n + m + 2);
    rep(i, n) {
        int k;
        cin >> k;
        rep(j, k) {
            int b;
            cin >> b;
            b--;
            mf.addedge(i, b + n, 1);
        }
    }
    rep(i, n) mf.addedge(S, i, 1);
    rep(j, m) mf.addedge(j + n, T, 1);
    ll res = mf.maxflow(S, T);
    cout << (res == m ? "Bob" : "Alice") << endl;
    return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 10988
final_rank: 13

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

typedef long long ll;
typedef pair<ll, ll> pr;
typedef pair<pr, ll> prpl;
typedef pair<ll, pr> prlp;

const ll INF = (ll)1e9;

#define rep(i, n) for (ll i = 0; i < (ll)(n); i++) // for文の省略
#define all(v) v.begin(), v.end() // 配列範囲の指定の省略。reverse()やsort()に入れる時便利

// 変数を宣言して入力を代入する所の省略
template<class... T>
void input(T&... a){
    (cin >> ... >> a);
}
#define llin(...) ll __VA_ARGS__; input(__VA_ARGS__)
void input_vector(vector<ll>& v, ll N){
    rep(i, N){
        cin >> v[i];
    }
}
#define vectin(v, N) vector<ll> v(N); input_vector(v, N)
template<class U>
void input_graph(vector<vector<U>>& G, ll M, bool directed = false, bool weighted = false){
    rep(i, M){
        if(!weighted){
            ll A, B; cin >> A >> B;
            if(!directed){
                G[A].push_back(B);
                G[B].push_back(A);
            } else {
                G[A].push_back(B);
            }
        } else {
            ll A, B, cost; cin >> A >> B >> cost;
            if(!directed){
                G[A].push_back({B, cost});
                G[B].push_back({A, cost});
            } else {
                G[A].push_back({B, cost});
            }
        }
    }
}
#define graphin(G, N, M) vector<vector<ll>> G(N); input_graph(G, M)
#define graphin_directed(G, N, M) vector<vector<ll>> G(N); input_graph(G, M, directed = true)
#define graphin_weighted(G, N, M) vector<vector<pr>> G(N); input_graph(G, M, weighted = true)
#define graphin_directed_weighted(G, N, M) vector<vector<pr>> G(N); input_graph(G, M, directed = true, weighted = true)
template<class U>
void input_edge(vector<U> E, ll M, bool weighted = false){
    rep(i, M){
        if(!weighted){
            ll A, B; cin >> A >> B;
            E[i] = {A, B};
        } else {
            ll A, B, cost; cin >> A >> B >> cost;
            E[i] = {cost, {A, B}};
        }
    }
    if(weighted){
        sort(all(E));
    }
}
#define edgein(E, M) vector<pr> E(M); input_edge(E, M)
#define edgein_weighted(E, M) vector<prlp> E(M); input_edge(E, M, weighted = true)


// デバッグ処理の簡略化。「#define _DEBUG」行をコメントアウトするかどうかで処理を行うかどうか変えられる
void debug_out() { cout << endl; }
template <typename Head, typename... Tail>
void debug_out(Head H, Tail... T) {
  cout << H << " ";
  debug_out(T...);
}
#define _DEBUG
#ifdef _DEBUG
#define debug(...) debug_out(__VA_ARGS__)
#else
#define debug(...) 
#endif

#ifndef H_make_vector
#define H_make_vector

/**
 * @brief 多次元 vector の作成
 * @author えびちゃん
 */

#include <cstddef>
#include <type_traits>
#include <vector>

namespace detail {
  template <typename Tp, ll Nb>
  auto make_vector(std::vector<ll>& sizes, Tp const& x) {
    if constexpr (Nb == 1) {
      return vector(sizes[0], x);
    } else {
      ll size = sizes[Nb-1];
      sizes.pop_back();
      return vector(size, make_vector<Tp, Nb-1>(sizes, x));
    }
  }
}  // detail::

template <typename Tp, ll Nb>
auto make_vector(ll const(&sizes)[Nb], Tp const& x = Tp()) {
  vector<ll> s(Nb);
  for (ll i = 0; i < Nb; ++i) s[i] = sizes[Nb-i-1];
  return detail::make_vector<Tp, Nb>(s, x);
}

#endif  /* !defined(H_make_vector) */

template <typename S, typename T>
pair<S, T> operator+(const pair<S, T> &p, const pair<S, T> &q) {
    return make_pair(p.first + q.first, p.second + q.second);
}
 
template <typename S, typename T>
pair<S, T> operator-(const pair<S, T> &p, const pair<S, T> &q) {
    return make_pair(p.first - q.first, p.second - q.second);
}

ll Max_V = 100000;
struct edge{
    ll to, cap, rev;
};
vector<vector<edge>> G(Max_V);
vector<bool> used(Max_V, false);

void add_edge(ll from, ll to, ll cap){
    if(to < 0) return;
    G[from].push_back((edge){to, cap, (ll)G[to].size()});
    G[to].push_back((edge){from, 0, (ll)G[from].size() - 1});
}

ll find_aug_path_DFS(ll v, ll t, ll f){
    if(v == t) return f;
    used[v] = true;
    rep(i, G[v].size()){
        edge& e = G[v][i];
        if(!used[e.to] && e.cap > 0){
            ll d = find_aug_path_DFS(e.to, t, min(f, e.cap));
            if(d > 0){
                e.cap -= d;
                G[e.to][e.rev].cap += d;
                return d;
            }
        }
    }
    return 0;
}

ll max_flow_FF(ll s, ll t){
    ll flow = 0;
    while(true){
        rep(i, Max_V){
            used[i] = false;
        }
        ll f = find_aug_path_DFS(s, t, INF);
        if(f == 0) return flow;
        flow += f;
    }
}

template<typename T> class Node {
public:
    pair<T, ll> val, al;
    T lazy;
    Node *left, *right, *par;
    Node() : val((T)0, -1), al((T)0, -1), lazy(0),
        left(nullptr), right(nullptr), par(nullptr){}
    void reset(){
        left = right = par = nullptr;
    }
    bool isRoot() const {
        return (!par) || (par->left != this && par->right != this);
    }
    void push(){
        if(lazy){
            val.first += lazy, al.first += lazy;
            if(left) left->lazy += lazy;
            if(right) right->lazy += lazy;
            lazy = 0;
        }
    }
    void eval(){
        al = val;
        if(left){
            left->push();
            if((left->al).first < al.first) al = left->al;
        }
        if(right){
            right->push();
            if((right->al).first <= al.first) al = right->al;
        }
    }
    void rotate(bool right){
        Node *p = this->par, *g = p->par;
        if(right){
            if((p->left = this->right)) this->right->par = p;
            this->right = p, p->par = this;
        }else{
            if((p->right = this->left)) this->left->par = p;
            this->left = p, p->par = this;
        }
        p->eval(), this->eval(), this->par = g;
        if(!g) return;
        if(g->left == p) g->left = this;
        if(g->right == p) g->right = this;
        g->eval();
    }
    void splay(){
        while(!(this->isRoot())){
            Node *p = this->par, *gp = p->par;
            if(p->isRoot()){
                p->push(), this->push();
                this->rotate((this == p->left));
            }else{
                gp->push(), p->push(), this->push();
                bool flag = (this == p->left);
                if((this == p->left) == (p == gp->left)){
                    p->rotate(flag), this->rotate(flag);
                }else{
                    this->rotate(flag), this->rotate(!flag);
                }
            }
        }
        this->push();
    }
};

template<typename T> class Dinic {
public:
    struct edge {
        const ll to, rev;
        T rev_cap;
        edge(const ll _to, const ll _rev, const T _rev_cap) :
            to(_to), rev(_rev), rev_cap(_rev_cap){}
    };
private:
    const ll V;
    vector<ll> que, level, iter, used;
	vector<Node<T> > arr;
    void access(Node<T> *u){
        Node<T> *last = nullptr;
        for(Node<T>* v = u; v; v = v->par){
            if((v->val).second == -1){
                last->par = nullptr;
                break;
            }
            v->splay(), v->right = last, v->eval(), last = v;
        }
        u->splay();
    }
    void link(Node<T> *u, Node<T> *v){
        access(u), u->right = v, u->eval(), v->par = u;
    }
    void set(Node<T> *u, const T x, const ll id){
        u->val = {x, id}, u->eval();
    }
    ll prev(Node<T> *u){
        (u = u->right)->push();
        while(u->left){
            (u = u->left)->push();
        }
        u->splay();
        return (u->val).second;
    }
    ll top(Node<T> *u){
        while(u->left){
            (u = u->left)->push();
        }
        u->splay();
        return (u->val).second;
    }
    void range(Node<T> *u, const T x){
        u->lazy += x, u->push();
    }
    void cut_and_reflect(const ll ver){
        Node<T> *u = &arr[ver];
        u->splay();
        if(u->left) u->left->par = nullptr, u->left = nullptr, u->eval();
        edge& e = G[ver][iter[ver]++];
        G[e.to][e.rev].rev_cap += e.rev_cap - (u->val).first, e.rev_cap = (u->val).first;
    }
    void bfs(const ll s, const ll t){
        level[s] = 0, used[s] = 1;
		ll qh = 0, qt = 0;
        for(que[qt++] = s; qh < qt;){
            ll v = que[qh++];
            for(edge& e : G[v]){
                if(level[e.to] < 0 && G[e.to][e.rev].rev_cap > 0){
					level[e.to] = level[v] + 1, used[e.to] = 1, que[qt++] = e.to;
                }
            }
        }
    }
	T block_flow_naive(const ll s, ll cur, T f){
		if(s == cur) return f;
		T flow = 0;
		for(ll& i = iter[cur]; i < (ll)G[cur].size(); ++i){
			edge& e = G[cur][i];
			if(e.rev_cap > 0 && level[e.to] < level[cur]){
				T d = block_flow_naive(s, e.to, min(f, e.rev_cap));
				if(d > 0){
					e.rev_cap -= d, G[e.to][e.rev].rev_cap += d;
					f -= d, flow += d;
					if(f == 0) break;
				}
			}
		}
		return flow;
	}
    T block_flow(const ll s, const ll t){
        T flow = 0;
        bool find = false;
        ll cur = t;
        while(true){
            if(find){
                const pair<T, ll>& res = arr[cur].al;
                flow += res.first;
                range(&arr[cur], -res.first);
                cut_and_reflect(cur = res.second);
                find = false;
            }
            used[cur] = 2;
            bool update = false;
            for(ll& i = iter[cur]; i < (ll)G[cur].size(); ++i){
                edge& e = G[cur][i];
                if(e.rev_cap > 0 && level[e.to] < level[cur] && used[e.to] >= 1){
                    update = true;
                    set(&arr[cur], e.rev_cap, cur);
                    if(e.to == s){
                        find = true;
                    }else if(used[e.to] == 1){
						arr[e.to].right = &arr[cur];
						arr[cur].par = &arr[e.to], cur = e.to;
					}else{
						link(&arr[e.to], &arr[cur]);
						const pair<T, ll>& res = arr[e.to].al;
						if(res.first == 0){
							cut_and_reflect(cur = res.second);
						}else{
							if(level[cur = top(&arr[e.to])] == 1) find = true;
							else cut_and_reflect(cur);
						}
					}
					break;
                }
            }
            if(update) continue;
            used[cur] = -1, (arr[cur].val).second = -1;
            if(cur == t) break;
            cut_and_reflect(cur = prev(&arr[cur]));
        }
        return flow;
    }
    void update_dfs(Node<T>* u, vector<vector<edge>>& G, const vector<ll>& iter, vector<ll>& used){
        u->push();
        if(u->left) update_dfs(u->left, G, iter, used);
        if(u->right) update_dfs(u->right, G, iter, used);
        const ll id = (u->val).second;
        if(iter[id] < (ll)G[id].size()){
            auto& e = G[id][iter[id]];
            G[e.to][e.rev].rev_cap += e.rev_cap - (u->val).first;
            e.rev_cap = (u->val).first;
        }
		used[id] = 0, u->reset();
    }
    void update_and_clear(vector<vector<edge>>& G, const vector<ll>& iter, vector<ll>& used){
        for(ll i = 0; i < V; ++i){
            if(used[i] == 2 && arr[i].isRoot()) update_dfs(&arr[i], G, iter, used);
			else if(used[i] == -1) arr[i].reset();
        }
    }

public:
    vector<vector<edge>> G;
    Dinic(const ll node_size)
        : V(node_size), que(V), level(V), iter(V), used(V), arr(V), G(V){}
    void add_edge(const ll from, const ll to, const T cap){
        G[from].emplace_back(to, (ll)G[to].size(), (T)0);
        G[to].emplace_back(from, (ll)G[from].size() - 1, cap);
    }
    T solve(const ll s, const ll t){
        T flow = 0;
        while(true){
            fill(level.begin(), level.end(), -1);
            fill(used.begin(), used.end(), 0);
            bfs(s, t);
            if(level[t] < 0) break;
            fill(iter.begin(), iter.end(), 0);
			flow += block_flow(s, t);
			update_and_clear(G, iter, used);
        }
        return flow;
    }
};



int main(void){
    llin(N, M);
    ll s = M + N, t = s + 1;
    Dinic<ll> Dn(M + N + 2);
    rep(i, M){
        Dn.add_edge(s, i, 1);
    }
    rep(i, N){
        Dn.add_edge(i + M, t, 1);
    }
    rep(i, N){
        ll K; cin >> K;
        rep(j, K){
            ll b; cin >> b; b--;
            Dn.add_edge(b, i + M, 1);
        }
    }
    ll Match = Dn.solve(s, t);
    if(Match < M){
        cout << "Alice" << endl;
    } else {
        cout << "Bob" << endl;
    }
}
accuracy: 1
time_ms: 30
memory_kb: 17588
final_rank: 16

code_snippet: #pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long int 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;
}

template<typename T>
struct dinic{
    struct edge{
        int to;
        T c,f;
    };
    T eps;
    const T inf=numeric_limits<T>::max();
    int n,m = 0;
    vector<edge> e;
    vector<vector<int>> g;
    vector<int> level, ptr;
    dinic(int n): n(n), g(n), level(n), ptr(n) {
        eps = (T)1 / (T)1e9;
    }
    void add_edge(int s, int t, T c){
        e.push_back({t, c, 0});
        e.push_back({s, 0, 0});
        g[s].push_back(m++);
        g[t].push_back(m++);
    }
    bool bfs(int s, int t){
        fill(level.begin(), level.end(), -1);
        level[s] = 0;
        for(queue<int> q({s});q.size();q.pop()){
            int s = q.front();
            for(int i:g[s]){
                int t = e[i].to;
                if(level[t] == -1 and (e[i].c - e[i].f) > eps){
                    level[t] = level[s] + 1;
                    q.push(t);
                }
            }
        }
        return (level[t] != -1);
    }
    T dfs(int s, int t, T psh){
        if(!(psh > eps) or s == t) return psh;
        for(int &i = ptr[s]; i < (int)g[s].size(); ++i){
            auto &eg = e[g[s][i]];
            if(level[eg.to] != level[s] + 1 or !(eg.c - eg.f > eps)) continue;
            T f = dfs(eg.to, t, min(psh, eg.c-eg.f));
            if(f > eps){
                eg.f += f;
                e[g[s][i]^1].f -= f;
                return f;
            }
        }
        return 0;
    }
    T max_flow(int s, int t){
        T f = 0;
        while(bfs(s,t)){
            fill(ptr.begin(), ptr.end(), 0);
            while(1){
                T c = dfs(s, t, inf);
                if(c > eps){
                    f += c;
                }
                else{
                    break;
                }
            }
        }
        return f;
    }
    // ABC239-G
    vector<bool> min_cut(int s){
        vector<bool> visited(n);
        queue<int> q; q.push(s);
        while(q.size()){
            int p = q.front(); q.pop();
            visited[p] = true;
            for(auto idx:g[p]){
                auto eg = e[idx];
                if(eg.c - eg.f > eps and !visited[eg.to]){
                    visited[eg.to] = true;
                    q.push(eg.to);
                }
            }
        }
        return visited;
    }
};

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    int n,m; cin >> n >> m;
    dinic<int> g(n+m+2);
    int S = n+m, T = n+m+1;
    for (int i = 0; i < n; i++) {
        int k; cin >> k;
        g.add_edge(i+m, T, 1);
        for (int j = 0; j < k; j++) {
            int a; cin >> a; a--;
            g.add_edge(a, i+m, 1);
        }
    }
    for (int i = 0; i < m; i++) {
        g.add_edge(S, i, 1);
    }
    if (g.max_flow(S,T) == m) {
        cout << "Bob" << endl;
    }
    else {
        cout << "Alice" << endl;
    }
}
accuracy: 1
time_ms: 10
memory_kb: 10992
final_rank: 4

code_snippet: #include <algorithm>
#include <cassert>
#include <limits>
#include <queue>
#include <vector>


#include <vector>

namespace atcoder {

namespace internal {

template <class T> struct simple_queue {
    std::vector<T> payload;
    int pos = 0;
    void reserve(int n) { payload.reserve(n); }
    int size() const { return int(payload.size()) - pos; }
    bool empty() const { return pos == int(payload.size()); }
    void push(const T& t) { payload.push_back(t); }
    T& front() { return payload[pos]; }
    void clear() {
        payload.clear();
        pos = 0;
    }
    void pop() { pos++; }
};

}  // namespace internal

}  // namespace atcoder


namespace atcoder {

template <class Cap> struct mf_graph {
  public:
    mf_graph() : _n(0) {}
    explicit mf_graph(int n) : _n(n), g(n) {}

    int add_edge(int from, int to, Cap cap) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        int m = int(pos.size());
        pos.push_back({from, int(g[from].size())});
        int from_id = int(g[from].size());
        int to_id = int(g[to].size());
        if (from == to) to_id++;
        g[from].push_back(_edge{to, to_id, cap});
        g[to].push_back(_edge{from, from_id, 0});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
    };

    edge get_edge(int i) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        auto _e = g[pos[i].first][pos[i].second];
        auto _re = g[_e.to][_e.rev];
        return edge{pos[i].first, _e.to, _e.cap + _re.cap, _re.cap};
    }
    std::vector<edge> edges() {
        int m = int(pos.size());
        std::vector<edge> result;
        for (int i = 0; i < m; i++) {
            result.push_back(get_edge(i));
        }
        return result;
    }
    void change_edge(int i, Cap new_cap, Cap new_flow) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        assert(0 <= new_flow && new_flow <= new_cap);
        auto& _e = g[pos[i].first][pos[i].second];
        auto& _re = g[_e.to][_e.rev];
        _e.cap = new_cap - new_flow;
        _re.cap = new_flow;
    }

    Cap flow(int s, int t) {
        return flow(s, t, std::numeric_limits<Cap>::max());
    }
    Cap flow(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);

        std::vector<int> level(_n), iter(_n);
        internal::simple_queue<int> que;

        auto bfs = [&]() {
            std::fill(level.begin(), level.end(), -1);
            level[s] = 0;
            que.clear();
            que.push(s);
            while (!que.empty()) {
                int v = que.front();
                que.pop();
                for (auto e : g[v]) {
                    if (e.cap == 0 || level[e.to] >= 0) continue;
                    level[e.to] = level[v] + 1;
                    if (e.to == t) return;
                    que.push(e.to);
                }
            }
        };
        auto dfs = [&](auto self, int v, Cap up) {
            if (v == s) return up;
            Cap res = 0;
            int level_v = level[v];
            for (int& i = iter[v]; i < int(g[v].size()); i++) {
                _edge& e = g[v][i];
                if (level_v <= level[e.to] || g[e.to][e.rev].cap == 0) continue;
                Cap d =
                    self(self, e.to, std::min(up - res, g[e.to][e.rev].cap));
                if (d <= 0) continue;
                g[v][i].cap += d;
                g[e.to][e.rev].cap -= d;
                res += d;
                if (res == up) return res;
            }
            level[v] = _n;
            return res;
        };

        Cap flow = 0;
        while (flow < flow_limit) {
            bfs();
            if (level[t] == -1) break;
            std::fill(iter.begin(), iter.end(), 0);
            Cap f = dfs(dfs, t, flow_limit - flow);
            if (!f) break;
            flow += f;
        }
        return flow;
    }

    std::vector<bool> min_cut(int s) {
        std::vector<bool> visited(_n);
        internal::simple_queue<int> que;
        que.push(s);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            visited[p] = true;
            for (auto e : g[p]) {
                if (e.cap && !visited[e.to]) {
                    visited[e.to] = true;
                    que.push(e.to);
                }
            }
        }
        return visited;
    }

  private:
    int _n;
    struct _edge {
        int to, rev;
        Cap cap;
    };
    std::vector<std::pair<int, int>> pos;
    std::vector<std::vector<_edge>> g;
};

}  // namespace atcoder

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

int main() {
    cin.tie(0)->sync_with_stdio(0);
    int n, m;
    cin >> n >> m;
    mf_graph<int> g(n + m + 2);
    auto s = 0, t = n + m + 1;
    for (auto i = 1; i <= n; ++i) {
        g.add_edge(s, i, 1);
    }
    for (auto i = n + 1; i <= n + m; ++i) {
        g.add_edge(i, t, 1);
    }
    for (auto i = 1; i <= n; ++i) {
        int k;
        cin >> k;
        for (auto j = 0; j < k; ++j) {
            int b;
            cin >> b;
            g.add_edge(i, n + b, 1);
        }
    }
    cout << (g.flow(s, t) == m ? "Bob\n" : "Alice\n");
}
accuracy: 1
time_ms: 10
memory_kb: 11212
final_rank: 8

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pll = pair<ll, ll>;
#define drep(i, cc, n) for (ll i = (cc); i <= (n); ++i)
#define rep(i, n) drep(i, 0, n - 1)
#define all(a) (a).begin(), (a).end()
#define pb push_back
#define fi first
#define se second

const ll MOD = 1000000007;
const ll MOD2 = 998244353;
const ll INF = 1LL << 60;
const ll MAX_N = 2e5;

struct Dinic{
    struct Edge{
        ll to;
        ll cap;
        ll rev;
    };

    vector<vector<Edge>> G;
    vector<ll> level;
    vector<ll> iter;
    ll N;

    Dinic(vector<vector<pll>> &G_){
        N = (ll)G_.size();
        G.resize(N);
        level.resize(N);
        iter.resize(N);

        rep(from, N){
            for(pll p : G_[from]){
                ll to = p.fi;
                ll cap = p.se;
                G[from].pb((Edge){to, cap, (ll)G[to].size()});
                G[to].pb((Edge){from, 0, (ll)G[from].size()-1});
            }
        }
    }

    void bfs(ll s, vector<vector<Edge>> &H){
        rep(i, N) level[i]=-1;

        queue<ll> Q;
        level[s] = 0;
        Q.push(s);
        while(!Q.empty()){
            ll v = Q.front();
            Q.pop();

            for(ll i=0; i<(ll)H[v].size(); i++){
                Edge &e = H[v][i];

                if(e.cap > 0 && level[e.to] < 0){
                    level[e.to] = level[v] + 1;
                    Q.push(e.to);
                }
            }
        }
    }

    ll dfs(ll v, ll t, ll f, vector<vector<Edge>> &H){
        if(v == t) return f;
        for(ll &i=iter[v]; i<(ll)H[v].size(); i++){
            Edge &e = H[v][i];
            if(e.cap > 0 && level[v] < level[e.to]){
                ll d = dfs(e.to, t, min(f, e.cap), H);
                if(d > 0){
                    e.cap -= d;
                    H[e.to][e.rev].cap += d;
                    return d;
                }
            }
        }

        return 0;
    }

    ll max_flow(ll s, ll t){
        vector<vector<Edge>> H(N);
        rep(from, N) for(Edge e : G[from]) H[from].pb(e);

        ll flow = 0;
        for(;;){
            bfs(s, H);
            if(level[t] < 0) return flow;
            rep(i, N) iter[i] = 0;
            ll f;
            while((f = dfs(s, t, INF, H)) > 0){
                flow += f;
            }
        }
    }
};

int main(){
    ll n, m;
    cin >> n >> m;

    ll s = n+m;
    ll t = n+m+1;
    vector<vector<pll>> G(n+m+2);
    rep(i, n) G[s].pb({i, 1});
    rep(i, n){
        ll k;
        cin >> k;
        rep(j, k){
            ll b;
            cin >> b;
            b--;
            G[i].pb({n+b, 1});
        }
    }
    rep(j, m) G[n+j].pb({t, 1});
    Dinic dinic(G);
    
    if(dinic.max_flow(s, t) == m){
        cout << "Bob" << endl;
    }else{
        cout << "Alice" << endl;
    }
}
accuracy: 1
time_ms: 30
memory_kb: 31624
final_rank: 18

code_snippet: #ifndef HIDDEN_IN_VS // 折りたたみ用

// 警告の抑制
#define _CRT_SECURE_NO_WARNINGS

// ライブラリの読み込み
#include <bits/stdc++.h>
using namespace std;

// 型名の短縮
using ll = long long; // -2^63 ～ 2^63 = 9 * 10^18（int は -2^31 ～ 2^31 = 2 * 10^9）
using pii = pair<int, int>;	using pll = pair<ll, ll>;	using pil = pair<int, ll>;	using pli = pair<ll, int>;
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 vb = vector<bool>;	using vvb = vector<vb>;		using vvvb = vector<vvb>;
using vc = vector<char>;	using vvc = vector<vc>;		using vvvc = vector<vvc>;
using vd = vector<double>;	using vvd = vector<vd>;		using vvvd = vector<vvd>;
template <class T> using priority_queue_rev = priority_queue<T, vector<T>, greater<T>>;
using Graph = vvi;

// 定数の定義
const double PI = acos(-1);
const vi DX = { 1, 0, -1, 0 }; // 4 近傍（下，右，上，左）
const vi DY = { 0, 1, 0, -1 };
int INF = 1001001001; ll INFL = 4004004004004004004LL;
double EPS = 1e-12;

// 入出力高速化
struct fast_io { fast_io() { cin.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(18); } } fastIOtmp;

// 汎用マクロの定義
#define all(a) (a).begin(), (a).end()
#define sz(x) ((int)(x).size())
#define lbpos(a, x) (int)distance((a).begin(), std::lower_bound(all(a), x))
#define ubpos(a, x) (int)distance((a).begin(), std::upper_bound(all(a), x))
#define Yes(b) {cout << ((b) ? "Yes\n" : "No\n");}
#define rep(i, n) for(int i = 0, i##_len = int(n); i < i##_len; ++i) // 0 から n-1 まで昇順
#define repi(i, s, t) for(int i = int(s), i##_end = int(t); i <= i##_end; ++i) // s から t まで昇順
#define repir(i, s, t) for(int i = int(s), i##_end = int(t); i >= i##_end; --i) // s から t まで降順
#define repe(v, a) for(const auto& v : (a)) // a の全要素（変更不可能）
#define repea(v, a) for(auto& v : (a)) // a の全要素（変更可能）
#define repb(set, d) for(int set = 0; set < (1 << int(d)); ++set) // d ビット全探索（昇順）
#define repp(a) sort(all(a)); for(bool a##_perm = true; a##_perm; a##_perm = next_permutation(all(a))) // a の順列全て（昇順）
#define smod(n, m) ((((n) % (m)) + (m)) % (m)) // 非負mod
#define uniq(a) {sort(all(a)); (a).erase(unique(all(a)), (a).end());} // 重複除去
#define EXIT(a) {cout << (a) << endl; exit(0);} // 強制終了

// 汎用関数の定義
template <class T> inline ll pow(T n, int k) { ll v = 1; rep(i, k) v *= n; return v; }
template <class T> inline bool chmax(T& M, const T& x) { if (M < x) { M = x; return true; } return false; } // 最大値を更新（更新されたら true を返す）
template <class T> inline bool chmin(T& m, const T& x) { if (m > x) { m = x; return true; } return false; } // 最小値を更新（更新されたら true を返す）

// 演算子オーバーロード
template <class T, class U> inline istream& operator>>(istream& is, pair<T, U>& p) { is >> p.first >> p.second; return is; }
template <class T> inline istream& operator>>(istream& is, vector<T>& v) { repea(x, v) is >> x; return is; }
template <class T> inline vector<T>& operator--(vector<T>& v) { repea(x, v) --x; return v; }
template <class T> inline vector<T>& operator++(vector<T>& v) { repea(x, v) ++x; return v; }

// 手元環境（Visual Studio）
#ifdef _MSC_VER
#include "local.hpp"
// 提出用（gcc）
#else
inline int popcount(int n) { return __builtin_popcount(n); }
inline int popcount(ll n) { return __builtin_popcountll(n); }
inline int lsb(int n) { return n != 0 ? __builtin_ctz(n) : -1; }
inline int lsb(ll n) { return n != 0 ? __builtin_ctzll(n) : -1; }
inline int msb(int n) { return n != 0 ? (31 - __builtin_clz(n)) : -1; }
inline int msb(ll n) { return n != 0 ? (63 - __builtin_clzll(n)) : -1; }
#define gcd __gcd
#define dump(...)
#define dumpel(v)
#define dump_list(v)
#define input_from_file(f)
#define output_to_file(f)
#define Assert(b) { if (!(b)) while (1) cout << "OLE"; }
#endif

#endif // 折りたたみ用


////--------------AtCoder 専用--------------
//#include <atcoder/all>
//using namespace atcoder;
//
////using mint = modint1000000007;
//using mint = modint998244353;
////using mint = modint; // mint::set_mod(m);
//
//istream& operator>>(istream& is, mint& x) { ll x_; is >> x_; x = x_; return is; }
//ostream& operator<<(ostream& os, const mint& x) { os << x.val(); return os; }
//using vm = vector<mint>; using vvm = vector<vm>; using vvvm = vector<vvm>;
////----------------------------------------


#ifndef ATCODER_INTERNAL_QUEUE_HPP
#define ATCODER_INTERNAL_QUEUE_HPP 1

#include <vector>

namespace atcoder {

    namespace internal {

        template <class T> struct simple_queue {
            std::vector<T> payload;
            int pos = 0;
            void reserve(int n) { payload.reserve(n); }
            int size() const { return int(payload.size()) - pos; }
            bool empty() const { return pos == int(payload.size()); }
            void push(const T& t) { payload.push_back(t); }
            T& front() { return payload[pos]; }
            void clear() {
                payload.clear();
                pos = 0;
            }
            void pop() { pos++; }
        };

    }  // namespace internal

}  // namespace atcoder

#endif  // ATCODER_INTERNAL_QUEUE_HPP


#ifndef ATCODER_MAXFLOW_HPP
#define ATCODER_MAXFLOW_HPP 1

#include <algorithm>
#include <cassert>
#include <limits>
#include <queue>
#include <vector>

namespace atcoder {

    template <class Cap> struct mf_graph {
    public:
        mf_graph() : _n(0) {}
        explicit mf_graph(int n) : _n(n), g(n) {}

        int add_edge(int from, int to, Cap cap) {
            assert(0 <= from && from < _n);
            assert(0 <= to && to < _n);
            assert(0 <= cap);
            int m = int(pos.size());
            pos.push_back({ from, int(g[from].size()) });
            int from_id = int(g[from].size());
            int to_id = int(g[to].size());
            if (from == to) to_id++;
            g[from].push_back(_edge{ to, to_id, cap });
            g[to].push_back(_edge{ from, from_id, 0 });
            return m;
        }

        struct edge {
            int from, to;
            Cap cap, flow;
        };

        edge get_edge(int i) {
            int m = int(pos.size());
            assert(0 <= i && i < m);
            auto _e = g[pos[i].first][pos[i].second];
            auto _re = g[_e.to][_e.rev];
            return edge{ pos[i].first, _e.to, _e.cap + _re.cap, _re.cap };
        }
        std::vector<edge> edges() {
            int m = int(pos.size());
            std::vector<edge> result;
            for (int i = 0; i < m; i++) {
                result.push_back(get_edge(i));
            }
            return result;
        }
        void change_edge(int i, Cap new_cap, Cap new_flow) {
            int m = int(pos.size());
            assert(0 <= i && i < m);
            assert(0 <= new_flow && new_flow <= new_cap);
            auto& _e = g[pos[i].first][pos[i].second];
            auto& _re = g[_e.to][_e.rev];
            _e.cap = new_cap - new_flow;
            _re.cap = new_flow;
        }

        Cap flow(int s, int t) {
            return flow(s, t, std::numeric_limits<Cap>::max());
        }
        Cap flow(int s, int t, Cap flow_limit) {
            assert(0 <= s && s < _n);
            assert(0 <= t && t < _n);
            assert(s != t);

            std::vector<int> level(_n), iter(_n);
            internal::simple_queue<int> que;

            auto bfs = [&]() {
                std::fill(level.begin(), level.end(), -1);
                level[s] = 0;
                que.clear();
                que.push(s);
                while (!que.empty()) {
                    int v = que.front();
                    que.pop();
                    for (auto e : g[v]) {
                        if (e.cap == 0 || level[e.to] >= 0) continue;
                        level[e.to] = level[v] + 1;
                        if (e.to == t) return;
                        que.push(e.to);
                    }
                }
            };
            auto dfs = [&](auto self, int v, Cap up) {
                if (v == s) return up;
                Cap res = 0;
                int level_v = level[v];
                for (int& i = iter[v]; i < int(g[v].size()); i++) {
                    _edge& e = g[v][i];
                    if (level_v <= level[e.to] || g[e.to][e.rev].cap == 0) continue;
                    Cap d =
                        self(self, e.to, std::min(up - res, g[e.to][e.rev].cap));
                    if (d <= 0) continue;
                    g[v][i].cap += d;
                    g[e.to][e.rev].cap -= d;
                    res += d;
                    if (res == up) return res;
                }
                level[v] = _n;
                return res;
            };

            Cap flow = 0;
            while (flow < flow_limit) {
                bfs();
                if (level[t] == -1) break;
                std::fill(iter.begin(), iter.end(), 0);
                Cap f = dfs(dfs, t, flow_limit - flow);
                if (!f) break;
                flow += f;
            }
            return flow;
        }

        std::vector<bool> min_cut(int s) {
            std::vector<bool> visited(_n);
            internal::simple_queue<int> que;
            que.push(s);
            while (!que.empty()) {
                int p = que.front();
                que.pop();
                visited[p] = true;
                for (auto e : g[p]) {
                    if (e.cap && !visited[e.to]) {
                        visited[e.to] = true;
                        que.push(e.to);
                    }
                }
            }
            return visited;
        }

    private:
        int _n;
        struct _edge {
            int to, rev;
            Cap cap;
        };
        std::vector<std::pair<int, int>> pos;
        std::vector<std::vector<_edge>> g;
    };

}  // namespace atcoder

#endif  // ATCODER_MAXFLOW_HPP


using namespace atcoder;


//【二部グラフの最大マッチング，最小辺被覆，最小点被覆】
/*
* 二部グラフ (S, T) の最大マッチングなどを求める．
*
* Bipartite_matching(int n, int m) : O(|V|)
*	S, T の要素数を n, m で初期化する．
*
* add_edge(int s, int t) : O(1)
*	s∈S と t∈T の間に辺を張る．
*
* int flow() : O( min(|V|^(2/3) (|V| + |E|), (|V| + |E|)^(3/2)) )
*	フローを流し計算を行う．
*	戻り値：最大マッチングの大きさ
*
* maximum_matching(vector<pii>& es) : O(|E|)
*	最大マッチングの例を具体的に求め es に格納する．
*	flow() の後に呼び出すこと．
* 	es : 最大マッチングに含まれる辺 {s, t} ∈ S×T のリスト
*
* minimum_edge_covering(vector<pii>& es) : O(|V| + |E|)
*	最小辺被覆の例を具体的に求め es に格納する．
*	es が最小辺被覆であるとは，任意の頂点がある e∈es の端点として現れることをいう．
*	flow() の後に呼び出すこと．
* 	es : 最小辺被覆に含まれる辺 {s, t} ∈ S×T のリスト
*
* minimum_vertex_covering(vvi& vs) : O(|V| + |E|)
*	最小点被覆の例を具体的に求め，S の頂点を vs[0], T の頂点を vs[1] に格納する．
*	vs が最小点被覆であるとは，任意の辺がある v∈vs を端点にもつことをいう．
*	flow() の後に呼び出すこと．
*
*（最大フロー問題）
*/
struct Bipartite_matching {
	// 参考 : https://qiita.com/drken/items/e805e3f514acceb87602
	// 参考 : https://qiita.com/drken/items/7f98315b56c95a6181a4

	int n, m;
	mf_graph<int> g;
	int ST, GL;

	// |S|, |T| を渡して初期化する．
	Bipartite_matching(int n_, int m_) : n(n_), m(m_) {
		g = mf_graph<int>(n + m + 2);

		// スタートとゴールおよびそれらとの間の辺を先に作っておく．
		ST = n + m;
		GL = n + m + 1;
		rep(i, n) g.add_edge(ST, i, 1);
		rep(j, m) g.add_edge(j + n, GL, 1);
	}

	// s∈S と t∈T の間に辺を張る． 
	void add_edge(int s, int t) { g.add_edge(s, t + n, 1); }

	// 計算を実行し，最大マッチングの大きさを返す．
	int flow() { return g.flow(ST, GL); }

	// 最大マッチングの例を具体的に求める．
	void maximum_matching(vector<pii>& es) {
		// verify : https://judge.yosupo.jp/problem/bipartitematching

		es.clear();

		repe(e, g.edges()) {
			// フローが流れている S, T 間の辺がマッチングに対応する．
			if (e.flow == 1 && e.from != ST && e.to != GL) {
				es.push_back({ e.from, e.to - n });
			}
		}
	}

	// 最小辺被覆の例を具体的に求める．
	void minimum_edge_covering(vector<pii>& es) {
		es.clear();

		// マッチングに含まれない S, T の頂点の集合
		unordered_set<int> iso_s, iso_t;
		rep(i, n) {
			if (g.get_edge(i).flow == 0) iso_s.insert(i);
		}
		rep(j, m) {
			if (g.get_edge(j + n).flow == 0) iso_t.insert(j + n);
		}

		repe(e, g.edges()) {
			// マッチングに含まれる S, T の頂点はそのまま結ぶ．
			if (e.flow == 1 && e.from != ST && e.to != GL) {
				es.push_back({ e.from, e.to - n });
			}
			// マッチングに含まれない S の頂点は，適当な T の頂点と結んでおく．
			else if (iso_s.count(e.from)) {
				es.push_back({ e.from, e.to - n });
				iso_s.erase(e.from);
			}
			// マッチングに含まれない T の頂点は，適当な S の頂点と結んでおく．
			else if (iso_t.count(e.to)) {
				es.push_back({ e.from, e.to - n });
				iso_t.erase(e.to);
			}
		}
	}

	// 最小点被覆の例を具体的に求める．
	void minimum_vertex_covering(vvi& vs) {
		// verify : https://judge.yosupo.jp/problem/assignment

		vs = vvi(2);

		// ar[v] : g の残余グラフで ST から v に到達可能か
		vb ar = g.min_cut(ST);

		// 残余グラフで ST から到達不可能な S の頂点を選ぶ．
		rep(i, n) {
			if (!ar[i]) vs[0].push_back(i);
		}

		// 残余グラフで ST から到達可能な T の頂点を選ぶ．
		rep(j, m) {
			if (ar[n + j]) vs[1].push_back(j);
		}
	}
};



int main() {
//	input_from_file("input.txt");
//	output_to_file("output.txt");

    int n, m;
    cin >> n >> m;

    Bipartite_matching g(n, m);
    rep(i, n) {
        int k;
        cin >> k;

        rep(hoge, k) {
            int j;
            cin >> j;
            j--;

            g.add_edge(i, j);
        }
    }

    int f = g.flow();

    cout << (f == m ? "Bob" : "Alice") << endl;
}
accuracy: 1
time_ms: 10
memory_kb: 11244
final_rank: 9

code_snippet: #line 1 "Contests/main.cpp"
/* #define aclmodint */
/* #define aclsegtree */
/* #include <atcoder/all>*/
/* using namespace atcoder; */

#line 1 "library/atcoder/maxflow.hpp"



#include <algorithm>
#include <cassert>
#include <limits>
#include <queue>
#include <vector>

#line 1 "library/atcoder/internal_queue.hpp"



#line 5 "library/atcoder/internal_queue.hpp"

namespace atcoder {

namespace internal {

template <class T> struct simple_queue {
    std::vector<T> payload;
    int pos = 0;
    void reserve(int n) { payload.reserve(n); }
    int size() const { return int(payload.size()) - pos; }
    bool empty() const { return pos == int(payload.size()); }
    void push(const T& t) { payload.push_back(t); }
    T& front() { return payload[pos]; }
    void clear() {
        payload.clear();
        pos = 0;
    }
    void pop() { pos++; }
};

}  // namespace internal

}  // namespace atcoder


#line 11 "library/atcoder/maxflow.hpp"

namespace atcoder {

template <class Cap> struct mf_graph {
  public:
    mf_graph() : _n(0) {}
    explicit mf_graph(int n) : _n(n), g(n) {}

    int add_edge(int from, int to, Cap cap) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        int m = int(pos.size());
        pos.push_back({from, int(g[from].size())});
        int from_id = int(g[from].size());
        int to_id = int(g[to].size());
        if (from == to) to_id++;
        g[from].push_back(_edge{to, to_id, cap});
        g[to].push_back(_edge{from, from_id, 0});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
    };

    edge get_edge(int i) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        auto _e = g[pos[i].first][pos[i].second];
        auto _re = g[_e.to][_e.rev];
        return edge{pos[i].first, _e.to, _e.cap + _re.cap, _re.cap};
    }
    std::vector<edge> edges() {
        int m = int(pos.size());
        std::vector<edge> result;
        for (int i = 0; i < m; i++) {
            result.push_back(get_edge(i));
        }
        return result;
    }
    void change_edge(int i, Cap new_cap, Cap new_flow) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        assert(0 <= new_flow && new_flow <= new_cap);
        auto& _e = g[pos[i].first][pos[i].second];
        auto& _re = g[_e.to][_e.rev];
        _e.cap = new_cap - new_flow;
        _re.cap = new_flow;
    }

    Cap flow(int s, int t) {
        return flow(s, t, std::numeric_limits<Cap>::max());
    }
    Cap flow(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);

        std::vector<int> level(_n), iter(_n);
        internal::simple_queue<int> que;

        auto bfs = [&]() {
            std::fill(level.begin(), level.end(), -1);
            level[s] = 0;
            que.clear();
            que.push(s);
            while (!que.empty()) {
                int v = que.front();
                que.pop();
                for (auto e : g[v]) {
                    if (e.cap == 0 || level[e.to] >= 0) continue;
                    level[e.to] = level[v] + 1;
                    if (e.to == t) return;
                    que.push(e.to);
                }
            }
        };
        auto dfs = [&](auto self, int v, Cap up) {
            if (v == s) return up;
            Cap res = 0;
            int level_v = level[v];
            for (int& i = iter[v]; i < int(g[v].size()); i++) {
                _edge& e = g[v][i];
                if (level_v <= level[e.to] || g[e.to][e.rev].cap == 0) continue;
                Cap d =
                    self(self, e.to, std::min(up - res, g[e.to][e.rev].cap));
                if (d <= 0) continue;
                g[v][i].cap += d;
                g[e.to][e.rev].cap -= d;
                res += d;
                if (res == up) return res;
            }
            level[v] = _n;
            return res;
        };

        Cap flow = 0;
        while (flow < flow_limit) {
            bfs();
            if (level[t] == -1) break;
            std::fill(iter.begin(), iter.end(), 0);
            Cap f = dfs(dfs, t, flow_limit - flow);
            if (!f) break;
            flow += f;
        }
        return flow;
    }

    std::vector<bool> min_cut(int s) {
        std::vector<bool> visited(_n);
        internal::simple_queue<int> que;
        que.push(s);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            visited[p] = true;
            for (auto e : g[p]) {
                if (e.cap && !visited[e.to]) {
                    visited[e.to] = true;
                    que.push(e.to);
                }
            }
        }
        return visited;
    }

  private:
    int _n;
    struct _edge {
        int to, rev;
        Cap cap;
    };
    std::vector<std::pair<int, int>> pos;
    std::vector<std::vector<_edge>> g;
};

}  // namespace atcoder


#line 7 "Contests/main.cpp"
using namespace atcoder;

#line 1 "library/me/template.cpp"
#include <bits/stdc++.h>
using namespace std;

#define REP(a,b) for(int a = 0;a < (int)(b);++a)
#define FOR(i,a,b) for(ll i = a; i < (ll)(b); i++)
#define ALL(a) (a).begin(),(a).end()
#define END(a) { print(a); return; }
#define DBG(a) { cerr << #a << ": "; dbg(a); }
using VI = vector<int>;
using VVI = vector<VI>;
using VVVI = vector<VVI>;
using ll = long long;
using VL = vector<ll>;
using VVL = vector<VL>;
using VVVL = vector<VVL>;
using VD = vector<double>;
using VVD = vector<VD>;
using VVVD = vector<VVD>;
using VS = vector<string>;
using VVS = vector<VS>;
using VVVS = vector<VVS>;
using VC = vector<char>;
using VVC = vector<VC>;
using VVVC = vector<VVC>;
using P = pair<int, int>;
using VP = vector<P>;
using VVP = vector<VP>;
using VVVP = vector<VVP>;
using LP = pair<ll, ll>;
using VLP = vector<LP>;
using VVLP = vector<VLP>;
using VVVLP = vector<VVLP>;
constexpr int INF = 1001001001;
constexpr ll LINF = 1001001001001001001ll;
constexpr int DX[] = {1, 0, -1, 0};
constexpr int DY[] = {0, 1, 0, -1};

#ifdef aclmodint

using MI7 = modint1000000007;
using V7 = vector<MI7>;
using VV7 = vector<V7>;
using VVV7 = vector<VV7>;
using MI3 = modint998244353;
using V3 = vector<MI3>;
using VV3 = vector<V3>;
using VVV3 = vector<VV3>;

ostream &operator<<(ostream &os, const modint &x) {
    os << x.val();
    return os;
}

ostream &operator<<(ostream &os, const MI3 &x) {
    os << x.val();
    return os;
}

ostream &operator<<(ostream &os, const MI7 &x) {
    os << x.val();
    return os;
}

istream &operator>>(istream &is, modint &x) {
    int y; is >> y;
    x = y;
    return is;
}

istream &operator>>(istream &is, MI3 &x) {
    int y; is >> y;
    x = y;
    return is;
}

istream &operator>>(istream &is, MI7 &x) {
    int y; is >> y;
    x = y;
    return is;
}

#endif

void print() { cout << '\n'; }
template<class T>
void print(const T &t) { cout << t << '\n'; }
template<class Head, class... Tail>
void print(const Head &head, const Tail &... tail) {
    cout << head << ' ';
    print(tail...);
}

void dbg() { cout << '\n'; }
template<class T>
void dbg(const T &t) { cerr << t << '\n'; }
template<class Head, class... Tail>
void dbg(const Head &head, const Tail &... tail) {
    cerr << head << ' ';
    dbg(tail...);
}

template< typename T1, typename T2 >
ostream &operator<<(ostream &os, const pair< T1, T2 >& p) {
    os << p.first << " " << p.second;
    return os;
}

template< typename T1, typename T2 >
istream &operator>>(istream &is, pair< T1, T2 > &p) {
    is >> p.first >> p.second;
    return is;
}

template< typename T >
ostream &operator<<(ostream &os, const vector< T > &v) {
    for(int i = 0; i < (int) v.size(); i++) {
    os << v[i] << (i + 1 != (int) v.size() ? " " : "");
    }
    return os;
}

template< typename T >
istream &operator>>(istream &is, vector< T > &v) {
    for(T &in : v) is >> in;
    return is;
}

template< typename T1, typename T2 >
inline bool chmax(T1 &a, T2 b) { return a < b && (a = b, true); }

template< typename T1, typename T2 >
inline bool chmin(T1 &a, T2 b) { return a > b && (a = b, true); }

template <typename T>
pair<VI, vector<T>> compress(const vector<T> &a) {
    int n = a.size();
    vector<T> x;
    REP(i, n) x.push_back(i);
    sort(ALL(x)); x.erase(unique(ALL(x)), x.end());
    VI res(n);
    REP(i, n) res[i] = lower_bound(ALL(x), a);
    return make_pair(res, x);
}

#ifdef aclsegtree
template<typename S>
struct value_size { S value; int size; };

template<typename S>
S min_op(S l, S r) { return min(l, r); };
template<typename S>
S max_op(S l, S r) { return max(l, r); };
template<typename S>
S sum_op(S l, S r) { return l + r; };
template<typename S>
value_size<S> sum_op_size(value_size<S> l, value_size<S> r) {
    return {l.value + r.value, l.size + r.size};
};
template<typename S>
value_size<S> min_op_size(value_size<S> l, value_size<S> r) {
    return {min(l.value, r.value), l.size + r.size};
};
template<typename S>
value_size<S> max_op_size(value_size<S> l, value_size<S> r) {
    return {max(l.value, r.value), l.size + r.size};
};

template<typename S>
S min_e() { return  numeric_limits<S>::max(); };
template<typename S>
S max_e() { return numeric_limits<S>::min(); };
template<typename S>
S sum_e() { return 0; }
template<typename S>
value_size<S> sum_e_size() { return {0, 0}; }
template<typename S>
value_size<S> min_e_size() { return {numeric_limits<S>::max(), 0}; }
template<typename S>
value_size<S> max_e_size() { return {numeric_limits<S>::min(), 0}; }

template<typename S, typename F>
S chmin_mapping(F f, S x) { return min(x, f); }
template<typename S, typename F>
S chmax_mapping(F f, S x) { return max(x, f); }
template<typename S, typename F>
S add_mapping(F f, S x) { return x + f; }
template<typename S, typename F>
value_size<S> add_mapping_size(F f, value_size<S> x) {
    return {x.value + x.size * f, x.size};
}

template<typename F>
F chmin_composition(F f, F g) { return min(f, g); }
template<typename F>
F chmax_composition(F f, F g) { return max(f, g); }
template<typename F>
F add_composition(F f, F g) { return f + g; }

template<typename F>
F chmin_id() { return numeric_limits<F>::max(); }
template<typename F>
F chmax_id() { return numeric_limits<F>::min(); }
template<typename F>
F add_id() { return 0; }

template<typename S>
using RSumQ = segtree<S, sum_op<S>, sum_e<S>>;
template<typename S>
using RMaxQ = segtree<S, max_op<S>, max_e<S>>;
template<typename S>
using RMinQ = segtree<S, min_op<S>, min_e<S>>;

template<typename S, typename F>
using RAddSumQ = lazy_segtree<value_size<S>, sum_op_size<S>, sum_e_size<S>,
    F, add_mapping_size<S, F>, add_composition<F>, add_id<F>>;
template<typename S, typename F>
using RAddMinQ = lazy_segtree<S, min_op<S>, min_e<S>,
    F, add_mapping<S, F>, add_composition<F>, add_id<F>>;
template<typename S, typename F>
using RAddMaxQ = lazy_segtree<S, max_op<S>, max_e<S>,
    F, add_mapping<S, F>, add_composition<F>, add_id<F>>;
template<typename S, typename F>
using RMinMinQ = lazy_segtree<S, min_op<S>, min_e<S>,
    F, chmin_mapping<S, F>, chmin_composition<F>, chmin_id<F>>;
template<typename S, typename F>
using RMaxMaxQ = lazy_segtree<S, max_op<S>, max_e<S>,
    F, chmax_mapping<S, F>, chmax_composition<F>, chmax_id<F>>;
#endif
#line 10 "Contests/main.cpp"

void solve(){
    while(1) {
        int n, m; cin >> n >> m;
        mf_graph<int> graph(n + m + 2);
        REP(i, n) {
            int k; cin >> k;
            REP(j, k) {
                int b; cin >> b; b--;
                graph.add_edge(i, n + b, 1);
            }
        }
        REP(i, n) graph.add_edge(n + m, i, 1);
        REP(i, m) graph.add_edge(n + i, n + m + 1, 1);
        print(graph.flow(n + m, n + m + 1) == m? "Bob" : "Alice");
        break;
    }
}

// generated by oj-template v4.7.2 (https://github.com/online-judge-tools/template-generator)
int main() {
    // Fasterize input/output script
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout << fixed << setprecision(100);
    // scanf/printf user should delete this fasterize input/output script

    int t = 1;
    //cin >> t; // comment out if solving multi testcase
    for(int testCase = 1;testCase <= t;++testCase){
        solve();
    }
    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 11144
final_rank: 6

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

#include <algorithm>

#include <vector>

namespace atcoder {

namespace internal {

template <class T> struct simple_queue {
    std::vector<T> payload;
    int pos = 0;
    void reserve(int n) { payload.reserve(n); }
    int size() const { return int(payload.size()) - pos; }
    bool empty() const { return pos == int(payload.size()); }
    void push(const T& t) { payload.push_back(t); }
    T& front() { return payload[pos]; }
    void clear() {
        payload.clear();
        pos = 0;
    }
    void pop() { pos++; }
};

}  // namespace internal

}  // namespace atcoder

#include <cassert>
#include <limits>
#include <queue>
#include <vector>

namespace atcoder {

template <class Cap> struct mf_graph {
  public:
    mf_graph() : _n(0) {}
    mf_graph(int n) : _n(n), g(n) {}

    int add_edge(int from, int to, Cap cap) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        int m = int(pos.size());
        pos.push_back({from, int(g[from].size())});
        g[from].push_back(_edge{to, int(g[to].size()), cap});
        g[to].push_back(_edge{from, int(g[from].size()) - 1, 0});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
    };

    edge get_edge(int i) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        auto _e = g[pos[i].first][pos[i].second];
        auto _re = g[_e.to][_e.rev];
        return edge{pos[i].first, _e.to, _e.cap + _re.cap, _re.cap};
    }
    std::vector<edge> edges() {
        int m = int(pos.size());
        std::vector<edge> result;
        for (int i = 0; i < m; i++) {
            result.push_back(get_edge(i));
        }
        return result;
    }
    void change_edge(int i, Cap new_cap, Cap new_flow) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        assert(0 <= new_flow && new_flow <= new_cap);
        auto& _e = g[pos[i].first][pos[i].second];
        auto& _re = g[_e.to][_e.rev];
        _e.cap = new_cap - new_flow;
        _re.cap = new_flow;
    }

    Cap flow(int s, int t) {
        return flow(s, t, std::numeric_limits<Cap>::max());
    }
    Cap flow(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);

        std::vector<int> level(_n), iter(_n);
        internal::simple_queue<int> que;

        auto bfs = [&]() {
            std::fill(level.begin(), level.end(), -1);
            level[s] = 0;
            que.clear();
            que.push(s);
            while (!que.empty()) {
                int v = que.front();
                que.pop();
                for (auto e : g[v]) {
                    if (e.cap == 0 || level[e.to] >= 0) continue;
                    level[e.to] = level[v] + 1;
                    if (e.to == t) return;
                    que.push(e.to);
                }
            }
        };
        auto dfs = [&](auto self, int v, Cap up) {
            if (v == s) return up;
            Cap res = 0;
            int level_v = level[v];
            for (int& i = iter[v]; i < int(g[v].size()); i++) {
                _edge& e = g[v][i];
                if (level_v <= level[e.to] || g[e.to][e.rev].cap == 0) continue;
                Cap d =
                    self(self, e.to, std::min(up - res, g[e.to][e.rev].cap));
                if (d <= 0) continue;
                g[v][i].cap += d;
                g[e.to][e.rev].cap -= d;
                res += d;
                if (res == up) break;
            }
            return res;
        };

        Cap flow = 0;
        while (flow < flow_limit) {
            bfs();
            if (level[t] == -1) break;
            std::fill(iter.begin(), iter.end(), 0);
            while (flow < flow_limit) {
                Cap f = dfs(dfs, t, flow_limit - flow);
                if (!f) break;
                flow += f;
            }
        }
        return flow;
    }

    std::vector<bool> min_cut(int s) {
        std::vector<bool> visited(_n);
        internal::simple_queue<int> que;
        que.push(s);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            visited[p] = true;
            for (auto e : g[p]) {
                if (e.cap && !visited[e.to]) {
                    visited[e.to] = true;
                    que.push(e.to);
                }
            }
        }
        return visited;
    }

  private:
    int _n;
    struct _edge {
        int to, rev;
        Cap cap;
    };
    std::vector<std::pair<int, int>> pos;
    std::vector<std::vector<_edge>> g;
};

}  // namespace atcoder

using namespace std;
using namespace atcoder;

using ll=long long;
#define rng(i,l,r) for(int i=int(l);i<int(r);i++)
#define rep(i,r) rng(i,0,r)
#define rrng(i,l,r) for(int i=int(r)-1;i>=int(l);i--)
#define rrep(i,r) rrng(i,0,r)
#define pb push_back
#define eb emplace_back
#define mp make_pair
#define mt make_tuple
#define F first
#define S second
#define bg begin()
#define ed end()
#define all(x) x.bg,x.ed
#define si(x) int(x.size())
#define inf INT_MAX/2-100
#define infl LLONG_MAX/3
#ifdef LOCAL
#define dmp(x) cerr<<__LINE__<<' '<<#x<<' '<<x<<endl
#else
#define dmp(x) void(0)
#endif

template<class t,class u>bool chmax(t&a,u b){if(a<b)a=b;return a<b;}
template<class t,class u>bool chmin(t&a,u b){if(b<a)a=b;return b<a;}

template<class t>using vc=vector<t>;
template<class t>using vvc=vector<vector<t>>;

using pi=pair<int,int>;
using pl=pair<ll,ll>;
using vi=vc<int>;
using vl=vc<ll>;


ll readl(void){
	ll x;
	cin>>x;
	return x;
}
int readi(void){
	int x;
	cin>>x;
	return x;
}

string readstr(){
	string s;
	cin>>s;
	return s;
}
vi readvi(int n,int off=0){
	vi v(n);
	rep(i,n)v[i]=readi(),v[i]+=off;
	return v;
}

vl readvl(int n,int off=0){
	vl v(n);
	rep(i,n)v[i]=readl(),v[i]+=off;
	return v;
}

template<class t>
void print(t x,int suc=1){
	cout<<x;
	if(suc==1)cout<<"\n";
	if(suc==2)cout<<" ";
}

template<class t>
void print(const vc<t>&v,int suc=1){
	rep(i,si(v))print(v[i],i==int(si(v))-1?1:suc);
}

template<class t>
bool inc(t a,t b,t c){
	return !(c<b||b<a);
}

template<class t>
void compress(vc<t>&v){
	sort(all(v));
	v.erase(unique(all(v)),v.ed);
}

template<class t>
int lwb(const vc<t>&v,const t&a){
	return lower_bound(all(v),a)-v.bg;
}

template<class t>
struct Compress{
	vc<t>v;
	Compress()=default;
	Compress(const vc<t>&x){
		add(x);
	}
	Compress(const initializer_list<vc<t> >&x){
		for(auto &p:x)add(p);
	}
	void add(const t&x){
		v.eb(x);
	}
	void add(const vc<t>&x){
		copy(all(x),back_inserter(v));
	}
	void build(){
		compress(v);
	}
	int get(const t&x)const{
		return lwb(v,x);
	}
	vc<t>get(const vc<t>&x)const{
		vc<t>res(x);
		for(auto &p:res)p=get(p);
		return res;
	}
	const t &operator[](int x)const{
		return v[x];
	}
	int size(){
		return v.size();
	}
};
void Yes(bool ex=true){
	cout<<"Yes\n";
	if(ex)exit(0);
}
void YES(bool ex=true){
	cout<<"YES\n";
	if(ex)exit(0);
}
void No(bool ex=true){
	cout<<"No\n";
	if(ex)exit(0);
}
void NO(bool ex=true){
	cout<<"NO\n";
	if(ex)exit(0);
}
void orYes(bool x,bool ex=true){
	if(x)Yes(ex);
	else No(ex);
}
void orYES(bool x,bool ex=true){
	if(x)YES(ex);
	else NO(ex);
}
void Possible(bool ex=true){
	cout<<"Possible\n";
	if(ex)exit(0);
}
void POSSIBLE(bool ex=true){
	cout<<"POSSIBLE\n";
	if(ex)exit(0);
}
void Impossible(bool ex=true){
	cout<<"Impossible\n";
	if(ex)exit(0);
}
void IMPOSSIBLE(bool ex=true){
	cout<<"IMPOSSIBLE\n";
	if(ex)exit(0);
}
void orPossible(bool x,bool ex=true){
	if(x)Possible(ex);
	else Impossible(ex);
}
void orPOSSIBLE(bool x,bool ex=true){
	if(x)POSSIBLE(ex);
	else IMPOSSIBLE(ex);
}

int n,m,s,t;
int main(void){
	cin.tie(0);
	ios::sync_with_stdio(0);
	cin>>n>>m;
	s=0,t=n+m+1;
	mf_graph<int>g(n+m+2);
	rng(i,1,n+1){
		g.add_edge(s,i,1);
		int x;
		cin>>x;
		rep(j,x){
			int y;
			cin>>y;
			g.add_edge(i,y+n,1);
		}
	}
	rng(i,1,m+1)g.add_edge(i+n,t,1);
	if(g.flow(s,t)==m)print("Bob");
	else print("Alice");
}
accuracy: 1
time_ms: 10
memory_kb: 11196
final_rank: 7

code_snippet: //#pragma GCC target("avx2,avx")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
//#include <atcoder/all>

#include <algorithm>

#include <vector>

namespace atcoder {

namespace internal {

template <class T> struct simple_queue {
    std::vector<T> payload;
    int pos = 0;
    void reserve(int n) { payload.reserve(n); }
    int size() const { return int(payload.size()) - pos; }
    bool empty() const { return pos == int(payload.size()); }
    void push(const T& t) { payload.push_back(t); }
    T& front() { return payload[pos]; }
    void clear() {
        payload.clear();
        pos = 0;
    }
    void pop() { pos++; }
};

}  // namespace internal

}  // namespace atcoder

#include <cassert>
#include <limits>
#include <queue>
#include <vector>

namespace atcoder {

template <class Cap> struct mf_graph {
  public:
    mf_graph() : _n(0) {}
    mf_graph(int n) : _n(n), g(n) {}

    int add_edge(int from, int to, Cap cap) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        int m = int(pos.size());
        pos.push_back({from, int(g[from].size())});
        g[from].push_back(_edge{to, int(g[to].size()), cap});
        g[to].push_back(_edge{from, int(g[from].size()) - 1, 0});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
    };

    edge get_edge(int i) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        auto _e = g[pos[i].first][pos[i].second];
        auto _re = g[_e.to][_e.rev];
        return edge{pos[i].first, _e.to, _e.cap + _re.cap, _re.cap};
    }
    std::vector<edge> edges() {
        int m = int(pos.size());
        std::vector<edge> result;
        for (int i = 0; i < m; i++) {
            result.push_back(get_edge(i));
        }
        return result;
    }
    void change_edge(int i, Cap new_cap, Cap new_flow) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        assert(0 <= new_flow && new_flow <= new_cap);
        auto& _e = g[pos[i].first][pos[i].second];
        auto& _re = g[_e.to][_e.rev];
        _e.cap = new_cap - new_flow;
        _re.cap = new_flow;
    }

    Cap flow(int s, int t) {
        return flow(s, t, std::numeric_limits<Cap>::max());
    }
    Cap flow(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);

        std::vector<int> level(_n), iter(_n);
        internal::simple_queue<int> que;

        auto bfs = [&]() {
            std::fill(level.begin(), level.end(), -1);
            level[s] = 0;
            que.clear();
            que.push(s);
            while (!que.empty()) {
                int v = que.front();
                que.pop();
                for (auto e : g[v]) {
                    if (e.cap == 0 || level[e.to] >= 0) continue;
                    level[e.to] = level[v] + 1;
                    if (e.to == t) return;
                    que.push(e.to);
                }
            }
        };
        auto dfs = [&](auto self, int v, Cap up) {
            if (v == s) return up;
            Cap res = 0;
            int level_v = level[v];
            for (int& i = iter[v]; i < int(g[v].size()); i++) {
                _edge& e = g[v][i];
                if (level_v <= level[e.to] || g[e.to][e.rev].cap == 0) continue;
                Cap d =
                    self(self, e.to, std::min(up - res, g[e.to][e.rev].cap));
                if (d <= 0) continue;
                g[v][i].cap += d;
                g[e.to][e.rev].cap -= d;
                res += d;
                if (res == up) break;
            }
            return res;
        };

        Cap flow = 0;
        while (flow < flow_limit) {
            bfs();
            if (level[t] == -1) break;
            std::fill(iter.begin(), iter.end(), 0);
            while (flow < flow_limit) {
                Cap f = dfs(dfs, t, flow_limit - flow);
                if (!f) break;
                flow += f;
            }
        }
        return flow;
    }

    std::vector<bool> min_cut(int s) {
        std::vector<bool> visited(_n);
        internal::simple_queue<int> que;
        que.push(s);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            visited[p] = true;
            for (auto e : g[p]) {
                if (e.cap && !visited[e.to]) {
                    visited[e.to] = true;
                    que.push(e.to);
                }
            }
        }
        return visited;
    }

  private:
    int _n;
    struct _edge {
        int to, rev;
        Cap cap;
    };
    std::vector<std::pair<int, int>> pos;
    std::vector<std::vector<_edge>> g;
};

}  // namespace atcoder

using namespace atcoder;
//using mint = modint998244353;
//using mint = modint1000000007;
#include <bits/stdc++.h>
using namespace std;
//#include <ext/pb_ds/assoc_container.hpp>
//#include <ext/pb_ds/tree_policy.hpp>
//using namespace __gnu_pbds;
//using i128 = __int128_t;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
using pll = pair<long long, long long>;
#define rep(i, n) for(int i = 0; i < (n); ++i)
#define all(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
constexpr char ln = '\n';
template<class T1, class T2> inline bool chmax(T1 &a, T2 b) {if (a < b) {a = b; return true;} return false;}
template<class T1, class T2> inline bool chmin(T1 &a, T2 b) {if (a > b) {a = b; return true;} return false;}
inline int topbit(int x) {return x == 0 ? -1 : 31-__builtin_clz(x);}
inline int topbit(long long x) {return x == 0 ? -1 : 63-__builtin_clzll(x);}
inline int botbit(int x) {return x == 0 ? 32 : __builtin_ctz(x);}
inline int botbit(long long x) {return x == 0 ? 64 : __builtin_ctzll(x);}
inline int popcount(int x) {return __builtin_popcount(x);}
inline int popcount(long long x) {return __builtin_popcountll(x);}
inline int kthbit(long long x, int k) {return (x>>k)&1;}
inline void print() {cout << "\n";}
template<class T>
inline void print(const vector<T> &v) {
    for (auto itr = v.begin(); itr != v.end(); ++itr) cout << *itr << " ";
    print();
}
template<class T, class... Args>
inline void print(const T &x, const Args &... args) {
    cout << x << " ";
    print(args...);
}
#ifdef MINATO_LOCAL
#define dump(x) cerr << __LINE__ << " : " << #x << " = " << (x) << endl;
inline void debug() {cerr << endl;}
template<class T>
inline void debug(const vector<T> &v) {
    for (auto itr = v.begin(); itr != v.end(); ++itr) cerr << *itr << " ";
    debug();
}
template<class T, class... Args>
inline void debug(const T &x, const Args &... args) {
    cerr << x << " ";
    debug(args...);
}
#else
#define dump(x) void(0)
inline void debug() {}
template<class T> inline void debug(const vector<T> &v) {}
template<class T, class... Args> inline void debug(const T &x, const Args &... args) {}
#endif
struct Fast_ios {Fast_ios() {cin.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(20);};} fast_ios;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int main() {
    int N,M; cin >> N >> M;
    if (M==0) {
        cout << "Bob" << ln;
        return 0;
    }
    if (N==0) {
        cout << "Alice" << ln;
        return 0;
    }
    int s = N+M;
    int t = N+M+1;
    mf_graph<int> g(N+M+2);
    rep(i,M) g.add_edge(s,i,1);
    rep(i,N) g.add_edge(M+i,t,1);
    rep(i,N) {
        int k; cin >> k;
        rep(j,k) {
            int v; cin >> v;
            --v;
            g.add_edge(v,M+i,1);
        }
    }

    cout << (g.flow(s,t)!=M ? "Alice" : "Bob") << ln;
}
accuracy: 1
time_ms: 10
memory_kb: 11108
final_rank: 5

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for(int i = 0; i < n; i++)
#define rep2(i, x, n) for(int i = x; i <= n; i++)
#define rep3(i, x, n) for(int i = x; i >= n; i--)
#define elif else if
#define sp(x) fixed << setprecision(x)
#define pb push_back
#define eb emplace_back
#define all(x) x.begin(), x.end()
#define sz(x) (int)x.size()
using ll = long long;
using ld = long double;
using pii = pair<int, int>;
using pil = pair<int, ll>;
using pli = pair<ll, int>;
using pll = pair<ll, ll>;
const ll MOD = 1e9+7;
//const ll MOD = 998244353;
const int inf = (1<<30)-1;
const ll INF = (1LL<<60)-1;
const ld EPS = 1e-10;
template<typename T> bool chmax(T &x, const T &y) {return (x < y)? (x = y, true) : false;};
template<typename T> bool chmin(T &x, const T &y) {return (x > y)? (x = y, true) : false;};

template<typename T>
struct Max_Flow{
    struct edge{
        int to; T cap; int rev;
        edge(int to, T cap, int rev) : to(to), cap(cap), rev(rev) {}
    };

    vector<vector<edge>> es;
    vector<bool> used;
    const T INF_T;

    Max_Flow(int n) : INF_T(numeric_limits<T>::max()){
        es.resize(n), used.resize(n);
    }

    void add_edge(int from, int to, T cap, bool directed = true){
        es[from].eb(to, cap, sz(es[to]));
        es[to].eb(from, directed? 0 : cap, sz(es[from])-1);
    }

    T dfs(int now, int t, T flow){
        if(now == t) return flow;
        used[now] = true;
        for(auto &e: es[now]){
            if(!used[e.to] && e.cap > 0){
                T f = dfs(e.to, t, min(flow, e.cap));
                if(f > 0){
                    e.cap -= f;
                    es[e.to][e.rev].cap += f;
                    return f;
                }
            }
        }
        return 0;
    }

    T max_flow(int s, int t){
        T flow = 0;
        for(;;){
            fill(all(used), false);
            T f = dfs(s, t, INF_T);
            if(f == 0) return flow;
            flow += f;
        }
    }
};

int main(){
    int N, M;
    cin >> N >> M;
    Max_Flow<int> G(N+M+2);
    int s = N+M, t = N+M+1;
    rep(i, N) G.add_edge(s, i, 1);
    rep(i, M) G.add_edge(N+i, t, 1);
    rep(i, N){
        int K; cin >> K;
        while(K--){
            int j; cin >> j; j--;
            G.add_edge(i, N+j, 1);
        }
    }
    cout << (G.max_flow(s, t) == M? "Bob" : "Alice") << endl;
}
accuracy: 1
time_ms: 110
memory_kb: 8800
final_rank: 17

code_snippet: //#define _GLIBCXX_DEBUG
#include <bits/stdc++.h>
#define rep(i, n) for(int i=0; i<n; ++i)
#define all(v) v.begin(), v.end()
#define rall(v) v.rbegin(), v.rend()
using namespace std;
using ll = int64_t;
using ld = long double;
using P = pair<int, int>;
using vs = vector<string>;
using vi = vector<int>;
using vvi = vector<vi>;
template<class T> using PQ = priority_queue<T>;
template<class T> using PQG = priority_queue<T, vector<T>, greater<T> >;
const int INF = 100010001;
const ll LINF = (ll)INF*INF*10;
template<typename T1, typename T2>
inline bool chmax(T1 &a, T2 b) {return a < b && (a = b, true);}
template<typename T1, typename T2>
inline bool chmin(T1 &a, T2 b) {return a > b && (a = b, true);}
template<typename T1, typename T2>
istream &operator>>(istream &is, pair<T1, T2> &p) { return is >> p.first >> p.second;}
template<typename T1, typename T2>
ostream &operator<<(ostream &os, const pair<T1, T2> &p) { return os << p.first << ' ' << p.second;}

struct BM {
  int V;
  vector<vector<int > > G;
  vector<int> match;
  vector<bool> used;
  BM(int n=0) : V(n), G(n), match(n, -1), used(n) {}
  inline void add(int u, int v) {
    G[u].emplace_back(v);
    G[v].emplace_back(u);
  }
  bool dfs(int v) {
    used[v] = true;
    for(int u:G[v]) {
      int w = match[u];
      if(w < 0 or (!used[w] and dfs(w))) {
        match[v] = u;
        match[u] = v;
        return true;
      }
    }
    return false;
  }
  int bipartite_matching() {
    int res = 0;
    for(int v = 0; v < V; v++) {
      if(match[v] < 0) {
        for(int i = 0; i < v; i++) used[i] = 0;
        if(dfs(v)) res++;
      }
    }
    return res;
  }
};

//head

int n, m;

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  cin >> n >> m;
  BM bpm(n+m);
  rep(i, n) {
    int k;
    cin >> k;
    while(k--) {
      int b;
      cin >> b;
      b--;
      bpm.add(i, b+n);
    }
  }
  bpm.bipartite_matching();
  for(int i = n; i < n+m; i++) if(bpm.match[i] == -1) {
    cout << "Alice" << endl;
    return 0;
  }
  cout << "Bob" << endl;
}
accuracy: 1
time_ms: 50
memory_kb: 4944
final_rank: 10

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;



#define NUM 1005

int V; //頂点数
vector<int> G[NUM]; //グラフの隣接リスト表現
int match[NUM]; //マッチングのペア
bool used[NUM]; //DFSですでに調べたかのフラグ

//fromとtoを結ぶ辺をグラフに追加する
void add_edge(int from,int to){
	G[from].push_back(to);
	G[to].push_back(from);
}

//増加パスをDFSで探す(node_idのペアを探す)
int dfs(int node_id){
	used[node_id] = true;

	for(int i = 0; i < G[node_id].size(); i++){
		int adj_node_id = G[node_id][i],pair_id = match[adj_node_id];
		if((pair_id < 0)|| //隣接するノードに、まだペアが存在しない
				(used[pair_id] == false && dfs(pair_id) == true)){

			match[node_id] = adj_node_id;
			match[adj_node_id] = node_id;
			return true;
		}
	}
	return false;
}


int bipartie_matching(){
	int ret = 0;
	for(int i = 0; i < V; i++)match[i] = -1; //ペアなしにしておく
	for(int node_id = 0; node_id < V; node_id++){
		if(match[node_id] < 0){ //node_idにペアがいない場合
			for(int i = 0; i < V; i++)used[i] = false;
			if(dfs(node_id)){ //増加パスが存在する場合
				ret++;
			}
		}
	}
	return ret;
}


int main(){

	int N,M;

	scanf("%d %d",&N,&M);

	int from,to;
	int tmp;

	for(int i = 0; i < N; i++){

		to = i;
		scanf("%d",&tmp);
		for(int k = 0; k < tmp; k++){

			scanf("%d",&from);
			from--;
			add_edge(from,M+to);
		}
	}

	V = N+M;

	if(bipartie_matching() == M){

		printf("Bob\n");

	}else{

		printf("Alice\n");
	}

	return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 4992
final_rank: 3

code_snippet: #include <stdio.h>
#include <algorithm>
#include <utility>
#include <functional>
#include <cstring>
#include <queue>
#include <stack>
#include <cmath>
#include <iterator>
#include <vector>
#include <string>
#include <set>
#include <iostream>
#include <random>
#include <map>
#include <iomanip>
#include <stdlib.h>
#include <list>
#include <typeinfo>
#include <list>
#include <set>
#include <cassert>
#include <fstream>
#include <unordered_map>
#include <cstdlib>
#include <complex>
#include <cctype>
#include <bitset>
using namespace std;

using ll = long long;
using vll = vector<ll>;
using pll = pair<ll, ll>;
using qll = queue<ll>;
using vb = vector<bool>;
using mll = map<ll, ll>;
using sll = stack<ll>;
#define REP(i,n) for(ll i(0);(i)<(n);(i)++)
#define rep(i,n) for(ll i(0);(i)<(n);(i)++)
#define ALL(a) a.begin(), a.end()
#define elnd endl //* missspell check
const ll INF = 1LL << 60;

void add_edge(vector<vll> &G, ll u, ll v){
    G[u].push_back(v); G[v].push_back(u);
}

bool dfs(vector<vll> &G, vb &checked, vll &match, ll v){
    //* match is initialized -1 outside
    checked[v] = true;
    REP(i, G[v].size()){
        ll u = G[v][i], w = match[u];
        if(w < 0 || !checked[w] && dfs(G, checked, match, w)){
            match[v] = u;
            match[u] = v;
            return true;
        }
    }
    return false;
}

ll BipartiteMatching(vector<vll> &G){
    ll N = G.size();
    vb checked(N, false);
    vll match(N, -1);
    ll res = 0;
    REP(v, N){
        if(match[v] < 0){
            fill(ALL(checked), false);
            if(dfs(G, checked, match, v)){
                res++;
            }
        }
    }
    return res;
}

int main(){
    ll N, M; cin >> N>>M;
    vector<vll> G(N+M);
    REP(i, N){
        ll K; cin >> K;
        REP(j, K){
            ll v; cin >> v; v --;
            add_edge(G, i, v+N);
        }
    }
    ll num_match=0;
    num_match = BipartiteMatching(G);
    if(num_match < M){
        cout<< "Alice"<<endl;
    }
    else{
        cout<< "Bob"<<endl;
    }

    return 0;
}
accuracy: 1
time_ms: 70
memory_kb: 7016
final_rank: 15

code_snippet: #include <algorithm>
#include <bitset>
#include <cassert>
#include <cctype>
#include <chrono>
#define _USE_MATH_DEFINES
#include <cmath>
#include <cstring>
#include <ctime>
#include <deque>
#include <functional>
#include <iostream>
#include <iomanip>
#include <iterator>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
using namespace std;

#define FOR(i,m,n) for(int i=(m);i<(n);++i)
#define REP(i,n) FOR(i,0,n)
#define ALL(v) (v).begin(),(v).end()

const int INF = 0x3f3f3f3f;
const long long LINF = 0x3f3f3f3f3f3f3f3fLL;
const double EPS = 1e-8;
const int MOD = 1000000007;
// const int MOD = 998244353;
const int dy[] = {1, 0, -1, 0}, dx[] = {0, -1, 0, 1};
// const int dy[] = {1, 1, 0, -1, -1, -1, 0, 1},
//           dx[] = {0, -1, -1, -1, 0, 1, 1, 1};

struct IOSetup {
  IOSetup() {
    cin.tie(nullptr);
    ios_base::sync_with_stdio(false);
    cout << fixed << setprecision(20);
    cerr << fixed << setprecision(10);
  }
} iosetup;
/*-------------------------------------------------*/
struct HopcroftKarp {
  vector<int> match;

  HopcroftKarp(int left, int right) : left(left), graph(left), match(left + right, -1), level(left), used(left, -1) {}

  void add_edge(int u, int v) { graph[u].emplace_back(left + v); }

  int solve() {
    int res = 0;
    while (true) {
      bfs();
      int tmp = 0;
      REP(i, left) if (match[i] == -1) {
        if (dfs(i)) ++tmp;
        ++timestamp;
      }
      if (tmp == 0) return res;
      res += tmp;
    }
  }

private:
  int left, timestamp = 0;
  vector<vector<int> > graph;
  vector<int> level, used;

  void bfs() {
    fill(ALL(level), -1);
    queue<int> que;
    REP(i, left) {
      if (match[i] == -1) {
        que.emplace(i);
        level[i] = 0;
      }
    }
    while (!que.empty()) {
      int ver = que.front(); que.pop();
      for (int e : graph[ver]) if (match[e] != -1 && level[match[e]] == -1) {
        level[match[e]] = level[ver] + 1;
        que.emplace(match[e]);
      }
    }
  }

  bool dfs(int ver) {
    used[ver] = timestamp;
    for (int e : graph[ver]) {
      int tmp = match[e];
      if (tmp == -1 || (used[tmp] < timestamp && level[tmp] == level[ver] + 1 && dfs(tmp))) {
        used[ver] = timestamp;
        match[e] = ver;
        match[ver] = e;
        return true;
      }
    }
    return false;
  }
};

int main() {
  int n, m; cin >> n >> m;
  HopcroftKarp hk(n, m);
  REP(i, n) {
    int k; cin >> k;
    while (k--) {
      int b; cin >> b; --b;
      hk.add_edge(i, b);
    }
  }
  cout << (hk.solve() == m ? "Bob\n" : "Alice\n");
  return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3708
final_rank: 1

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

#define rep(i, n) for (int (i) = 0 ; (i) < (int)(n) ; ++(i))
#define repn(i, m, n) for (int (i) = (m) ; (i) < (int)(n) ; ++(i))
#define rep_rev(i, n) for (int (i) = (int)(n) - 1 ; (i) >= 0 ; --(i))
#define repn_rev(i, m, n) for (int (i) = (int)(n) - 1 ; (i) >= (m) ; --(i))

#define INF 2e9
#define ll long long
#define MOD 1e9+7
#define MAX_V 20005

/* 辺を表す構造体 */
struct edge {int to, cap ,rev; };

/* グラフの隣接リスト表現 */
vector<edge> G[MAX_V];
/* DFSですべに調べたかのグラフ */
bool used[MAX_V];

/* fromからtoへ向かう容量capの辺をグラフに追加する */
void add_edge(int from, int to, int cap){
    G[from].push_back((edge){to, cap, (int)G[to].size()});
    G[to].push_back((edge){from, 0, (int)G[from].size()-1});    
}


/* 増加パスをdfsで探す */
int dfs(int v, int t, int f){
    if(v==t) return f;
    used[v] = true;
    for(int i=0; i<G[v].size(); i++){
        edge &e = G[v][i];
        if(!used[e.to] && e.cap>0){
            int d = dfs(e.to, t, min(f, e.cap));
            if(d>0){
                e.cap -= d;
                G[e.to][e.rev].cap += d;
                return d;
            }
        }
    }
    return 0;
}

int max_flow(int s, int t){
    int flow = 0;
    for(;;){
        memset(used, 0, sizeof(used));
        int f = dfs(s, t, INF);
        if(f==0) return flow;
        flow += f;
    }
}

int main(){
    int n, m;
    cin>>n>>m;
    for(int i=1; i<=n; i++){
        /* add_edge(0, i, 1); */
        add_edge(i, 0, 1);
    }
    for(int i=n+1; i<=n+m; i++){
        /* add_edge(i, n+m+1, 1); */
        add_edge(n+m+1, i, 1);
    }
    rep(i, n){
        int k;
        cin>>k;
        rep(j, k){
            int l;
            cin>>l;
            add_edge(n+l, i+1, 1);
        }
    }

    if(max_flow(m+n+1, 0) == m){
        cout<<"Bob"<<endl;
    }
    else{
        cout<<"Alice"<<endl;
    }

    return 0;
}
accuracy: 1
time_ms: 160
memory_kb: 9584
final_rank: 19

code_snippet: // aribon3-5_b
#include <bits/stdc++.h>
#ifdef LOCAL
#include "../cxx-prettyprint/prettyprint.hpp"
#endif
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> P;

#define REP(i, n) for (int (i) = 0 ; (i) < (int)(n) ; ++(i))
#define REPN(i, m, n) for (int (i) = m ; (i) < (int)(n) ; ++(i))
#define REP_REV(i, n) for (int (i) = (int)(n) - 1 ; (i) >= 0 ; --(i))
#define REPN_REV(i, m, n) for (int (i) = (int)(n) - 1 ; (i) >= m ; --(i))
#define ALL(x) x.begin(), x.end()

#define INF ((1 << 29)-1)
#define MOD (1000000007)

#define print2D(h, w, arr) REP(i, h) { REP(j, w) cout << arr[i][j] << " "; cout << endl; }
template<class T> void print(const T& x){cout << x << endl;}
template<class T, class... A> void print(const T& first, const A&... rest) { cout << first << " "; print(rest...); }
struct PreMain {PreMain(){cin.tie(0);ios::sync_with_stdio(false);cout<<fixed<<setprecision(20);}} premain;

// dinic
template <typename T = long long>
struct Dinic {
    struct edge { int to; T cap; int rev; };
    vector<vector<edge>> G;
    vector<int> itr, level;
    int n;
    T inf;

    explicit Dinic(int n): n(n), G(n) {inf=numeric_limits<T>::max();}

    void add_edge(int from, int to, T cap) {
        G[from].push_back((edge) {to, cap, (int) G[to].size()});
        G[to].push_back((edge) {from, 0, (int) G[from].size()-1});
    }

    void bfs(int s) {
        level.assign(n, -1);
        queue<int> q;
        level[s] = 0;
        q.push(s);
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            for (auto &e: G[v]) {
                if (e.cap > 0 and level[e.to] < 0) {
                    level[e.to] = level[v] + 1;
                    q.push(e.to);
                }
            }
        }
    }

    T dfs(int v, int t, T f) {
        if (v == t) return f;
        for (int& i = itr[v]; i < (int) G[v].size(); ++i) {
            edge& e = G[v][i];
            if (e.cap > 0 and level[v] < level[e.to]) {
                T d = dfs(e.to, t, min(f, e.cap));
                if (d > 0) {
                    e.cap -= d;
                    G[e.to][e.rev].cap += d;
                    return d;
                }
            }
        }
        return 0;
    }

    T max_flow(int s, int t) {
        T ret = 0, f;
        while (bfs(s), level[t] >= 0) {
            itr.assign(n, 0);
            while ((f = dfs(s, t, inf)) > 0) ret += f;
        }
        return ret;
    }
};

int main() {
#ifdef LOCAL
    ifstream in("../arg.txt"); cin.rdbuf(in.rdbuf());
#endif

    int N, M;
    cin >> N >> M;

    auto dinic = Dinic<ll>(N+M+2);

    int s = N+M, t = N+M+1;
    REP(i, N) dinic.add_edge(s, i, 1);
    REP(j, M) dinic.add_edge(N+j, t, 1);

    REP(i, N){
        int k; cin >> k;
        REP(j, k) {
            int b; cin >> b; b--;
            dinic.add_edge(i, N+b, 1);
        }
    }

    int match = dinic.max_flow(s, t);

    if (match == M){
        print("Bob");
    } else {
        print("Alice");
    }

    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 14928
final_rank: 14

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

using namespace std;
typedef long long ll;
typedef vector<int> VI;
typedef vector<VI> VVI;
typedef vector<ll> VL;
typedef vector<VL> VVL;
typedef pair<int, int> PII;

#define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i)
#define REP(i, n) FOR(i, 0, n)
#define ALL(x) x.begin(), x.end()
#define IN(a, b, x) (a<=x&&x<b)
#define MP make_pair
#define PB push_back
const int INF = (1LL<<30);
const ll LLINF = (1LL<<60);
const double PI = 3.14159265359;
const double EPS = 1e-12;
const int MOD = 1000000007;
//#define int ll

template <typename T> T &chmin(T &a, const T &b) { return a = min(a, b); }
template <typename T> T &chmax(T &a, const T &b) { return a = max(a, b); }

int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};

#define MAX_V 1000
int V;
VI G[MAX_V];
int match[MAX_V];
bool used[MAX_V];

void add_edge(int u, int v) {
  G[u].PB(v);
  G[v].PB(u);
}

bool dfs(int v) {
  used[v] = true;
  REP(i, G[v].size()) {
    int u = G[v][i], w = match[u];
    if(w < 0 || !used[w] && dfs(w)) {
      match[v] = u;
      match[u] = v;
      return true;
    }
  }
  return false;
}

int bipartite_matching() {
  int res = 0;
  memset(match, -1, sizeof(match));
  REP(v, V) {
    if(match[v] < 0) {
      memset(used, 0, sizeof(used));
      if(dfs(v)) res++;
    }
  }
  return res;
}

signed main(void)
{
  int n, m;
  cin >> n >> m;
  REP(i, n) {
    int k;
    cin >> k;
    REP(j, k) {
      int b;
      cin >> b;
      b--;
      add_edge(i, n+b);
      // cout << i << " " << n+b << endl;
    }
  }

  V = n+m;
  // cout << bipartite_matching() << endl;
  if(bipartite_matching() == m) cout << "Bob" << endl;
  else cout << "Alice" << endl;

  return 0;
}
accuracy: 1
time_ms: 60
memory_kb: 5008
final_rank: 12

code_snippet: #include "bits/stdc++.h"
using namespace std;
#ifdef _DEBUG
#include "dump.hpp"
#else
#define dump(...)
#endif

//#define int long long
#define rep(i,a,b) for(int i=(a);i<(b);i++)
#define rrep(i,a,b) for(int i=(b)-1;i>=(a);i--)
#define all(c) begin(c),end(c)
const int INF = sizeof(int) == sizeof(long long) ? 0x3f3f3f3f3f3f3f3fLL : 0x3f3f3f3f;
const int MOD = (int)(1e9) + 7;
template<class T> bool chmax(T &a, const T &b) { if (a < b) { a = b; return true; } return false; }
template<class T> bool chmin(T &a, const T &b) { if (b < a) { a = b; return true; } return false; }

struct HopcroftKarp {
	int n;
	vector<vector<int>> g;
	vector<int> match, dist;
	vector<bool> used, alive;
	HopcroftKarp(int n) : n(n), g(n), match(n), dist(n), used(n), alive(n, true) {}
	void addEdge(int u, int v) {
		g[u].emplace_back(v);
		g[v].emplace_back(u);
	}
	int maximumMatching() {
		int cnt = 0;
		fill(match.begin(), match.end(), -1);
		while (true) {
			buildAlternatingLevelGraph();
			fill(used.begin(), used.end(), false);
			int flow = 0;
			for (int u = 0; u < n; u++) {
				if (!alive[u])continue;
				if (match[u] == -1 && augment(u))
					flow++;
			}
			if (flow == 0)break;
			cnt += flow;
		}
		return cnt;
	}
	void buildAlternatingLevelGraph() {
		fill(dist.begin(), dist.end(), -1);
		queue<int> q;
		for (int u = 0; u < n; u++)
			if (match[u] == -1) {
				q.emplace(u);
				dist[u] = 0;
			}
		while (q.size()) {
			int u = q.front(); q.pop();
			for (int v : g[u]) {
				int w = match[v];
				if (w != -1 && dist[w] == -1) {
					dist[w] = dist[u] + 1;
					q.emplace(w);
				}
			}
		}
	};
	bool augment(int u) {
		used[u] = true;
		for (int v : g[u]) {
			if (!alive[v])continue;
			int w = match[v];
			if (w == -1 || (!used[w] && dist[w] == dist[u] + 1 && augment(w))) {
				match[u] = v;
				match[v] = u;
				used[v] = true;
				return true;
			}
		}
		return false;
	}
};

signed main() {
	cin.tie(0);
	ios::sync_with_stdio(false);
	int N, M; cin >> N >> M;
	HopcroftKarp hk(N + M);
	rep(i, 0, N) {
		int K; cin >> K;
		rep(j, 0, K) {
			int b; cin >> b; b--;
			hk.addEdge(i, N + b);
		}
	}
	int res = hk.maximumMatching();
	if (res == M) {
		cout << "Bob" << endl;
	}
	else {
		cout << "Alice" << endl;
	}
	return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 4816
final_rank: 2

code_snippet: // テ・ツ淞コテヲツ慊ャテ」ツδ?」ツδウテ」ツδ療」ツδャテ」ツδシテ」ツδ?(テァツクツョテ・ツーツ湘ァツ可?

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int (i)=(a); (i)<(n); (i)++)
#define repq(i,a,n) for(int (i)=(a); (i)<=(n); (i)++)
#define repr(i,a,n) for(int (i)=(a); (i)>=(n); (i)--)
#define int long long
template<typename T> void chmax(T &a, T b) {a = max(a, b);}
template<typename T> void chmin(T &a, T b) {a = min(a, b);}
template<typename T> void chadd(T &a, T b) {a = a + b;}
typedef pair<int, int> pii;
typedef long long ll;
constexpr ll INF = 1001001001001001LL;
constexpr ll MOD = 1000000007LL;

// テ、ツコツ古ゥツδィテ」ツδ榲」ツδε」ツδ?」ツδウテ」ツつー (テヲツ慊?・ツ、ツァテヲツオツ?」ツつ津、ツスツソテ」ツ?」テ」ツ?ヲティツァツ」テ」ツ??
// テ」ツδサテヲツ卍ョテゥツ?堙」ツ?ォティツセツコテ」ツつ津・ツシツオテ」ツつ凝、ツサツ姪」ツ?ォテ」ツ??ィツ?ェテ・ツ按?」ツ?ァテ・ツ按・テ」ツ?ォテゥツ?づァツつケテ」ツつ?2 テ」ツ?、テァツ板ィテヲツ?湘」ツ?凖」ツつ?

template <typename T>
struct Edge {
    int to; T cap, cost; int rev;
    Edge(int t, T c, int r) : to(t), cap(c), rev(r) {}
    Edge(int t, T ca, T co, int r) : to(t), cap(ca), cost(co), rev(r) {}
};

template <typename T>
using Graph = vector< vector< Edge<T> > >;

struct BipartiteMatching {
    int S, T, E, a, b;
    vector< vector< Edge<int> > > G;
    vector<int> used;
    BipartiteMatching(int x, int y, int z) : S(x), T(y), E(z) {
        G.resize(S+T+2);
        used.resize(S+T+2);
        a = S + T, b = a + 1;
    }
    void add_edge(int from, int to, int cap) {
        G[from].push_back(Edge<int>(to  , cap, G[to  ].size()    ));
        G[to  ].push_back(Edge<int>(from, 0  , G[from].size() - 1));
    }
    void readGraph() {
        for(int i=0; i<S; i++) add_edge(a  , i, 1);
        for(int i=0; i<T; i++) add_edge(i+S, b, 1);
    }
    int dfs(int v, int t, int f) {
        if(v == t) return f;
        used[v] = true;
        for(size_t i=0; i<G[v].size(); i++) {
            Edge<int> &e = G[v][i];
            if(!used[e.to] && e.cap > 0) {
                int d = dfs(e.to, t, min(f, e.cap));
                if(d > 0) {
                    e.cap -= d;
                    G[e.to][e.rev].cap += d;
                    return d;
                }
            }
        }
        return 0;
    }
    int solve() {
        int flow = 0;
        while(1) {
            fill(used.begin(), used.end(), 0);
            int f = dfs(a, b, INF);
            if(f == 0) return flow;
            flow += f;
        }
    }
};

int N, M;

signed main() {
    cin >> N >> M;
    BipartiteMatching fl(N, M, 0);
    rep(i,0,N) {
        int p; cin >> p;
        rep(j,0,p) {
            int d; cin >> d; d--;
            fl.add_edge(i, N+d, 1);
        }
    }
    fl.readGraph();
    cout << (fl.solve() == M ? "Bob" : "Alice") << endl;
    return 0;
}
accuracy: 1
time_ms: 130
memory_kb: 19132
final_rank: 20