Problem:
Problem I: Shiritori
Problem
ここに文字列のリストがある。息抜きに、しりとりをして遊ぶことにしよう。しりとりは、以下のルールで行われる。
まず最初に、リストの中から好きな文字列を一つ選び、その文字列をリストから除外する。
続いて、一つ前に選んだ文字列の最後の一文字が、最初の一文字である文字列をリストから一つ選ぶ。
選んだ文字列をリストから除外する。
この後、2., 3.を交互に繰り返すことになる。
さて、このしりとりを2.でリストから選べる文字列がなくなるまで続けたとしよう。
  このときに、最後に選んだ文字列の「最後の一文字」としてあり得る文字をすべて列挙したい。
Input
$N$
$s_1$
$s_2$
$s_3$
:
$s_N$
一行目に、文字列の数$N$が与えられる。
続く$N$行に、文字列のリストが与えられる。
Constraints
入力は以下の条件を満たす。
$1 \le N \lt 10^4$
$1 \le |s_i| \lt 100$
$s_i \neq s_j  (i \neq j)$
文字列には、英小文字のみ含まれる
Output
最後に選んだ文字列の「最後の一文字」としてあり得る文字をaからzの順で辞書順で昇順に出力せよ。
Sample Input 1
5
you
ate
my
hum
toast
Sample Output 1
e
t
u
この時、以下のような場合がアウトプット例となる。
ate(1つの文字列のみ)
toast
hum - my - you
また、m,yで終わるパターンは存在しない。
Sample Input 2
7
she
sells
sea
shells
by
the
seashore
Sample Output 2
a
e
y

Submitted solutions:

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <set>
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>>;


struct Dinic {
    struct edge {
        int to, rev; ll cap;
        edge(int t, int r, ll c) : to(t), rev(r), cap(c) {}
    };
    int n;
    vii<edge> to;
    vi<int> dist, iter;
    const ll inf = 1e18;
    Dinic(int n_) : n(n_), to(n_), dist(n_), iter(n_) {}

    void addedge(int u, int v, ll c) {
        int su = (int)to[u].size();
        int sv = (int)to[v].size();
        to[u].push_back({ v, sv, c });
        to[v].push_back({ u, su, 0 });
    }

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

    ll dfs(int v, int t, ll f) {
        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 (nv.cap == 0 || dist[nv.to] <= dist[v]) continue;
            ll 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;
    }

    ll maxflow(int s, int t) {
        ll res = 0, flow = 0;
        while (true) {
            bfs(s);
            if (dist[t] < 0) break;
            iter.assign(n, 0);
            do {
                flow = dfs(s, t, inf);
                res += flow;

            } while (flow);
        };
        return res;
    }
};

int main()
{
    std::cin.tie(nullptr);
    std::ios_base::sync_with_stdio(false);
    int n;
    cin >> n;

    vi<string> s(n);
    rep(i, n) cin >> s[i];

    //文字cを頂点とみなし、indeg: cで終わる、outdeg: cで始まる
    int mx = 26;
    vi<int> indeg(mx), outdeg(mx);
    rep(i, n) {
        indeg[s[i].back() - 'a']++;
        outdeg[s[i].front() - 'a']++;
    }

    vi<char> ans;
    rep(i, mx) {
        char c = 'a' + i;

        //cで終わるものがそもそもない
        if (indeg[i] == 0) continue;

        //cに戻ってくることはない
        if (outdeg[i] == 0) {
            ans.push_back('a' + i);
            continue;
        }

        //cで始まるのものがcで終わるものより多い
        if (indeg[i] < outdeg[i]) continue;

        Dinic dn(mx + 2);
        int S = mx, T = S + 1;
        
        rep(j, n) {
            char sf = s[j].front();
            char sb = s[j].back();
            int u = sf - 'a';
            int v = sb - 'a';
            if (sf == c) u = S;
            if (sb == c) v = T;
            dn.addedge(u, v, 1);
        }

        int flow = dn.maxflow(S, T);
        if (flow == outdeg[i]) ans.push_back(c);
    }

    for (char c : ans) cout << c << endl;
    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 5136
final_rank: 4

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <set>
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>>;


struct Dinic {
    struct edge {
        int to, rev; ll cap;
        edge(int t, int r, ll c) : to(t), rev(r), cap(c) {}
    };
    int n;
    vii<edge> to;
    vi<int> dist, iter;
    const ll inf = 1e18;
    Dinic(int n_) : n(n_), to(n_), dist(n_), iter(n_) {}

    void addedge(int u, int v, ll c) {
        int su = (int)to[u].size();
        int sv = (int)to[v].size();
        to[u].push_back({ v, sv, c });
        to[v].push_back({ u, su, 0 });
    }

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

    ll dfs(int v, int t, ll f) {
        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 (nv.cap == 0 || dist[nv.to] <= dist[v]) continue;
            ll 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;
    }

    ll maxflow(int s, int t) {
        ll res = 0, flow = 0;
        while (true) {
            bfs(s);
            if (dist[t] < 0) break;
            iter.assign(n, 0);
            do {
                flow = dfs(s, t, inf);
                res += flow;

            } while (flow);
        };
        return res;
    }
};

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

    vi<string> s(n);
    rep(i, n) cin >> s[i];

    //文字cを頂点とみなし、indeg: cで終わる、outdeg: cで始まる
    int mx = 26;
    vi<int> indeg(mx), outdeg(mx);
    rep(i, n) {
        indeg[s[i].back() - 'a']++;
        outdeg[s[i].front() - 'a']++;
    }

    vi<char> ans;
    rep(i, mx) {
        char c = 'a' + i;

        //cで終わるものがそもそもない
        if (indeg[i] == 0) continue;

        //cに戻ってくることはない
        if (outdeg[i] == 0) {
            ans.push_back('a' + i);
            continue;
        }

        Dinic dn(mx + 2);
        int S = mx, T = S + 1;
        
        rep(j, n) {
            char sf = s[j].front();
            char sb = s[j].back();
            int u = sf - 'a';
            int v = sb - 'a';
            if (sf == c) u = S;
            if (sb == c) v = T;
            dn.addedge(u, v, 1);
        }

        int flow = dn.maxflow(S, T);
        if (flow >= outdeg[i]) ans.push_back(c);
    }

    for (char c : ans) cout << c << endl;
    return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 5476
final_rank: 8

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;
typedef tuple<ll, ll, ll> tri;
typedef tuple<ll, ll, ll, ll, ll> cinq;


const ll INF = (ll)1e18;

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

// 変数を宣言して入力を代入する所の省略
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 = 603;


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




const ll alpha = 26;

int main(void){
    llin(N);
    Dinic<ll> Dn(2 * alpha);
    vector<ll> delta_minus(alpha, 0), delta_plus(alpha, 0);
    rep(i, N){
        string S; cin >> S;
        char s_c = S[0], t_c = S[S.size() - 1];
        ll a = s_c - 'a', b = t_c - 'a';
        Dn.add_edge(a + alpha, b, 1);
        delta_plus[a]++; delta_minus[b]++;
    }
    rep(i, alpha){
        Dn.add_edge(i, i + alpha, INF);
    }
    rep(i, alpha){
        if(delta_minus[i] != 0 && Dn.solve(i + alpha, i) == delta_plus[i]){
            cout << (char)('a' + i) << endl;
        }
    }
    
}
accuracy: 1
time_ms: 10
memory_kb: 3984
final_rank: 2

code_snippet: #include<bits/stdc++.h>
// #include<atcoder/all>
using namespace std;
// using namespace atcoder;
// #include "atcoder/modint.hpp"
// using mint = atcoder::modint998244353;
#define int long long
#define intu __int128_t
#define rep(i,n) for(int i=0;i<n;++i)
#define rep1(i,n) for(int i=1;i<=n;++i)
#define reps(i,a,b,c) for(int i=a;i<b;i+=c)
#define repnega(i,a,b) for(int i=b;i>a;i--)
#define mod2 1000000007
#define mod 998244353
#define endl '\n'
#define AC ios_base::sync_with_stdio(false);cin.tie(0);
#define pb push_back
#define eb emplace_back
#define ep emplace

#pragma GCC target("avx2")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")


int n;
string s[10009];
int sh[10009][2];
vector<int> g[10009];
vector<int> revg[10009];
vector<int> ans(29,0);
void dfs(int p,int c){
	sh[p][c]=1;
	int cnt=0;
	for(int e:g[p]){
		if(!sh[e][c]){dfs(e,c);cnt++;}
	}
	if(cnt==0&&sh[p][1]==0){
		c=1;
		sh[p][1]=1;
		if(c==1){
			ans[s[p][(int)s[p].size()-1]-'a']=1;
			// cout<<s[p]<<endl;
		}
		for(int e:g[p]){
			if(!sh[e][c]){dfs(e,c);cnt++;}
		}
	}
}
signed main(){
	cin>>n;
	rep(i,n)cin>>s[i];
	rep(i,n)rep(j,n){
		if(i==j)continue;
		if(s[i][(int)s[i].size()-1]==s[j][0]){
			g[i].pb(j);
			revg[j].pb(i);
		}
	}
	rep(i,n){
		if(revg[i].size()==0&&(sh[i][0]==0&&sh[i][1]==0)){
			dfs(i,0);
		}
	}
	rep(i,n){
		if((int)g[i].size()&&sh[i][0]==0&&sh[i][1]==0){
			dfs(i,0);
		}
	}
	rep(i,26){
		if(ans[i]){
			cout<<(char)('a'+i)<<endl;
		}
	}
}
accuracy: 0.1794871794871795
time_ms: 10
memory_kb: 9236
final_rank: 20

code_snippet: #include<bits/stdc++.h>
// #include<atcoder/all>
using namespace std;
// using namespace atcoder;
// #include "atcoder/modint.hpp"
// using mint = atcoder::modint998244353;
#define int long long
#define intu __int128_t
#define rep(i,n) for(int i=0;i<n;++i)
#define rep1(i,n) for(int i=1;i<=n;++i)
#define reps(i,a,b,c) for(int i=a;i<b;i+=c)
#define repnega(i,a,b) for(int i=b;i>a;i--)
#define mod2 1000000007
#define mod 998244353
#define endl '\n'
#define AC ios_base::sync_with_stdio(false);cin.tie(0);
#define pb push_back
#define eb emplace_back
#define ep emplace

#pragma GCC target("avx2")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")


int n;
string s[10009];
int sh[10009][2];
vector<int> g[10009];
vector<int> revg[10009];
vector<int> ans(29,0);
void dfs(int p,int c){
	sh[p][c]=1;
	int cnt=0;
	for(int e:g[p]){
		if(!sh[e][c]){dfs(e,c);cnt++;}
	}
	if(cnt==0&&sh[p][1]==0){
		c=1;
		sh[p][1]=1;
		if(c==1){
			ans[s[p][(int)s[p].size()-1]-'a']=1;
			// cout<<s[p]<<endl;
		}
		for(int e:g[p]){
			if(!sh[e][c]){dfs(e,c);cnt++;}
		}
	}
}
signed main(){
	cin>>n;
	rep(i,n)cin>>s[i];
	rep(i,n)rep(j,n){
		if(i==j)continue;
		if(s[i][(int)s[i].size()-1]==s[j][0]){
			g[i].pb(j);
			revg[j].pb(i);
		}
	}
	rep(i,n){
		if(revg[i].size()==0&&(sh[i][0]==0&&sh[i][1]==0)){
			dfs(i,0);
		}
	}
	rep(i,n){
		if(sh[i][0]==0&&sh[i][1]==0){
			dfs(i,0);
		}
	}
	rep(i,26){
		if(ans[i]){
			cout<<(char)('a'+i)<<endl;
		}
	}
}
accuracy: 0.1794871794871795
time_ms: 10
memory_kb: 9212
final_rank: 19

code_snippet: #pragma region Macros

#include <bits/extc++.h>
using namespace std;
using namespace __gnu_pbds;
// using namespace __gnu_cxx;

// #include <boost/multiprecision/cpp_int.hpp>
// namespace mp = boost::multiprecision;
// using Bint = mp::cpp_int;

#define TO_STRING(var) # var
#define pb emplace_back
#define int ll
#define endl '\n'

using ll = long long;
using ld = long double;
const ld PI = acos(-1);
const ld EPS = 1e-10;
const int INF = 1 << 30;
const ll INFL = 1LL << 61;
// const int MOD = 998244353;
const int MOD = 1000000007;

// __attribute__((constructor))
// void constructor() {
//     ios::sync_with_stdio(false);
//     cin.tie(nullptr);
//     // ifstream in("input.txt");
//     // cin.rdbuf(in.rdbuf());
//     cout << fixed << setprecision(15);
// }

template<int mod> class modint{
public:
    int val = 0;
    modint(int x = 0) { while (x < 0) x += mod; val = x % mod; }
    modint(const modint &r) { val = r.val; }

    modint operator -() { return modint(-val); }
    modint operator +(const modint &r) { return modint(*this) += r; }
    modint operator -(const modint &r) { return modint(*this) -= r; }
    modint operator *(const modint &r) { return modint(*this) *= r; }
    modint operator /(const modint &r) { return modint(*this) /= r; }

    modint &operator +=(const modint &r) {
        val += r.val;
        if (val >= mod) val -= mod;
        return *this;
    }
    modint &operator -=(const modint &r) {
        if (val < r.val) val += mod;
        val -= r.val;
        return *this;
    }
    modint &operator *=(const modint &r) {
        val = val * r.val % mod;
        return *this;
    }
    modint &operator /=(const modint &r) {
        int a = r.val, b = mod, u = 1, v = 0;
        while (b) {
            int t = a / b;
            a -= t * b; swap(a, b);
            u -= t * v; swap(u, v);
        }
        val = val * u % mod;
        if (val < 0) val += mod;
        return *this;
    }

    bool operator ==(const modint& r) { return this -> val == r.val; }
    bool operator <(const modint& r) { return this -> val < r.val; }
    bool operator !=(const modint& r) { return this -> val != r.val; }
};

using mint = modint<MOD>;

istream &operator >>(istream &is, mint& x) {
    int t; is >> t;
    x = t;
    return (is);
}
ostream &operator <<(ostream &os, const mint& x) {
    return os << x.val;
}

mint modpow(const mint &a, int n) {
    if (n == 0) return 1;
    mint t = modpow(a, n / 2);
    t = t * t;
    if (n & 1) t = t * a;
    return t;
}

int modpow(int x, int n, int mod) {
    int ret = 1;
    while (n > 0) {
        if (n % 2 == 1) ret = ret * x % mod;
        x = x * x % mod;
        n /= 2;
    }
    return ret;
}

int ceil(int x, int y) { return (x > 0 ? (x + y - 1) / y : x / y); }

#pragma endregion

struct Edge {
    char from, to;
    int cap, rev;
    Edge(char to, int cap, int rev) : from(-1), to(to), cap(cap), rev(rev) {}
    Edge(char from, char to, int cap, int rev) : from(from), to(to), cap(cap), rev(rev) {}
    Edge &operator=(const int &x) {
        to = x;
        return *this;
    }

    // operator int() const { return to; }
};

vector<int> flag;
int dfs(int v, int t, int f, vector<vector<Edge>> &G) { // tは一貫
    if (v == t) return f; // 残余グラフ上にs-tパスが存在するなら
    flag[v] = true;

    for (auto &e : G[v]) {
        if (flag[e.to] or e.cap == 0) continue;
        // eが訪問済み、もしくは残余グラフの辺eが無効ならとばす

        int d = dfs(e.to, t, min(f, e.cap), G); // fはtに到達するまでずっと0
        if (d > 0) {
            e.cap -= d; // 残余グラフの容量を減らし
            G[e.to][e.rev].cap += d; // 元のグラフで同じ量の水を長す
            return d;
        }
    }
    return 0; // s-tパスが見つからなかったらこれが返る
}

int max_flow(int s, int t, vector<vector<Edge>> &G) {
    int ret = 0;

    while (true) {
        flag.assign((int)G.size(), false);
        int f = dfs(s, t, INFL, G);
        if (f == 0) return ret; // 残余グラフでs-tパスが存在しなくなったら終了
        ret += f;
    }
    // そもそも元のグラフにs-tパスが存在しない場合は0が返る
}

signed main() {
    int N;
    cin >> N;
    vector<string> S(N);
    for (int i = 0; i < N; i++) cin >> S[i];

    vector<vector<Edge>> G(270);
    for (int i = 0; i < N; i++) {
        G[S[i][0]].pb(S[i].back(), 1, G[S[i].back()].size());
        G[S[i].back()].pb(S[i][0], 0, G[S[i][0]].size() - 1); // 逆辺
    }

    vector<vector<Edge>> G2 = G;

    for (char c = 'a'; c <= 'z'; c++) {
        G = G2;
        char t = 'z' + 1;
        int indeg = 0;
        int outdeg = 0;
        for (int i = 0; i < N; i++) {
            if (S[i].back() == c) {
                G[S[i][0]].pb(t, 1, G[t].size());
                G[t].pb(S[i][0], 0, G[S[i][0]].size() - 1); // 逆辺
                indeg++;
            }
            if (S[i][0] == c) outdeg++;
        }
        if (indeg == outdeg && indeg == 0) continue;
        // cout << c << " " << outdeg << " " << max_flow(c, t, G) << endl;

        if (max_flow(c, t, G) == outdeg) cout << c << endl;
    }
}
accuracy: 1
time_ms: 190
memory_kb: 6020
final_rank: 12

code_snippet: #pragma region Macros

#include <bits/extc++.h>
using namespace std;
using namespace __gnu_pbds;
// using namespace __gnu_cxx;

// #include <boost/multiprecision/cpp_int.hpp>
// namespace mp = boost::multiprecision;
// using Bint = mp::cpp_int;

#define TO_STRING(var) # var
#define pb emplace_back
#define int ll
#define endl '\n'

using ll = long long;
using ld = long double;
const ld PI = acos(-1);
const ld EPS = 1e-10;
const int INF = 1 << 30;
const ll INFL = 1LL << 61;
// const int MOD = 998244353;
const int MOD = 1000000007;

// __attribute__((constructor))
// void constructor() {
//     ios::sync_with_stdio(false);
//     cin.tie(nullptr);
//     // ifstream in("input.txt");
//     // cin.rdbuf(in.rdbuf());
//     cout << fixed << setprecision(15);
// }

template<int mod> class modint{
public:
    int val = 0;
    modint(int x = 0) { while (x < 0) x += mod; val = x % mod; }
    modint(const modint &r) { val = r.val; }

    modint operator -() { return modint(-val); }
    modint operator +(const modint &r) { return modint(*this) += r; }
    modint operator -(const modint &r) { return modint(*this) -= r; }
    modint operator *(const modint &r) { return modint(*this) *= r; }
    modint operator /(const modint &r) { return modint(*this) /= r; }

    modint &operator +=(const modint &r) {
        val += r.val;
        if (val >= mod) val -= mod;
        return *this;
    }
    modint &operator -=(const modint &r) {
        if (val < r.val) val += mod;
        val -= r.val;
        return *this;
    }
    modint &operator *=(const modint &r) {
        val = val * r.val % mod;
        return *this;
    }
    modint &operator /=(const modint &r) {
        int a = r.val, b = mod, u = 1, v = 0;
        while (b) {
            int t = a / b;
            a -= t * b; swap(a, b);
            u -= t * v; swap(u, v);
        }
        val = val * u % mod;
        if (val < 0) val += mod;
        return *this;
    }

    bool operator ==(const modint& r) { return this -> val == r.val; }
    bool operator <(const modint& r) { return this -> val < r.val; }
    bool operator !=(const modint& r) { return this -> val != r.val; }
};

using mint = modint<MOD>;

istream &operator >>(istream &is, mint& x) {
    int t; is >> t;
    x = t;
    return (is);
}
ostream &operator <<(ostream &os, const mint& x) {
    return os << x.val;
}

mint modpow(const mint &a, int n) {
    if (n == 0) return 1;
    mint t = modpow(a, n / 2);
    t = t * t;
    if (n & 1) t = t * a;
    return t;
}

int modpow(int x, int n, int mod) {
    int ret = 1;
    while (n > 0) {
        if (n % 2 == 1) ret = ret * x % mod;
        x = x * x % mod;
        n /= 2;
    }
    return ret;
}

int ceil(int x, int y) { return (x > 0 ? (x + y - 1) / y : x / y); }

#pragma endregion

signed main() {
    int N;
    cin >> N;
    vector<string> S(N);
    for (int i = 0; i < N; i++) cin >> S[i];

    map<string, string> G;
    map<string, int> outdeg;
    map<string, int> indeg;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            if (i == j) continue;
            if (S[i].back() == S[j][0]) {
                G[S[i]] = S[j];
                outdeg[S[i]]++;
                indeg[S[j]]++;
            }
        }
    }

    set<char> ans;
    for (int i = 0; i < N; i++) {
        if (outdeg[S[i]] == 0) ans.insert(S[i].back());
    }
    for (auto &x : ans) {
        cout << x << endl;
    }
   
}
accuracy: 0.1794871794871795
time_ms: 40
memory_kb: 4980
final_rank: 18

code_snippet: #line 1 "library/me/template.cpp"
// This is a dummy line
#ifndef ONLINE_JUDGE
#define _GLIBCXX_DEBUG
#endif

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

#define REP(i, n) for(int i = 0; i < (int)(n); i++)
#define FOR(i, a, b) for(ll i = a; i < (ll)(b); i++)
#define ALL(a) (a).begin(),(a).end()
#define END(...) { print(__VA_ARGS__); return; }

#ifdef ONLINE_JUDGE
#define DBG(...) ;
#else
#define DBG(a) { cerr << #a << ": "; dbg(a); }
#endif

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<typename T>
void print(const T &t) { cout << t << '\n'; }
template<typename Head, typename... Tail>
void print(const Head &head, const Tail &... tail) {
    cout << head << ' ';
    print(tail...);
}

#ifdef ONLINE_JUDGE
template<typename... Args>
void dbg(const Args &... args) {}
#else
void dbg() { cerr << '\n'; }
template<typename T>
void dbg(const T &t) { cerr << t << '\n'; }
template<typename Head, typename... Tail>
void dbg(const Head &head, const Tail &... tail) {
    cerr << head << ' ';
    dbg(tail...);
}
#endif

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(a[i]);
    sort(ALL(x)); x.erase(unique(ALL(x)), x.end());
    VI res(n);
    REP(i, n) res[i] = lower_bound(ALL(x), a[i]) - x.begin();
    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 2 "Contests/Dummy/a/main.cpp"
/* #define aclmodint */
/* #define aclsegtree */
/* #include <atcoder/all> */
/* using namespace atcoder; */
#line 1 "library/atcoder/atcoder/maxflow.hpp"



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

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



#line 5 "library/atcoder/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/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/Dummy/a/main.cpp"
using namespace atcoder;

void solve(){
    int n; cin >> n;
    VS s(n); cin >> s;
    VI leave(26);
    VI sink(26);
    REP(i, n) {
        leave[s[i][0] - 'a']++;
        sink[s[i].back() - 'a']++;
    }
    REP(i, 26) {
        mf_graph<int> mfg(28);
        REP(j, n) {
            int from = s[j][0] - 'a';
            int to = s[j].back() - 'a';
            if(from == i) from = 26;
            if(to == i) to = 27;
            mfg.add_edge(from, to, 1);
        }
        if(sink[i] > 0 && mfg.flow(26, 27) == leave[i]) print((char)('a' + i));
    }
}

// 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: 20
memory_kb: 5096
final_rank: 5

code_snippet: //////////////////////////////
// Check before you submit.
// #define _ATCODER_LIBRARY

const long long MOD = 1e9+7;
// const long long MOD = 998244353;

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

#ifdef _ATCODER_LIBRARY
#include <atcoder/all>
using namespace atcoder;
#endif
// _ATCODER_LIBRARY

const long long INF = 1LL << 60;
const double PI = acos(-1);
using ll = long long;
using P = array<ll, 2>;

#define FOR(i,a,b) for (ll i=(a);i<(ll)(b);++i)
#define REP(i,n) FOR(i,0,n)
#define ALL(v) (v).begin(),(v).end()
#define SUM(v) accumulate(ALL(v),0ll)

template<typename T>istream& operator>>(istream&i,vector<T>&v){REP(j,v.size())i>>v[j];return i;}
template<typename T>string join(vector<T>&v){stringstream s;REP(i,v.size())s<<' '<<v[i];return s.str().substr(1);}
template<typename T>ostream& operator<<(ostream&o,vector<T>&v){if(v.size())o<<join(v);return o;}
template<typename T>string join(vector<vector<T>>&vv){string s="\n";REP(i,vv.size())s+=join(vv[i])+"\n";return s;}
template<typename T>ostream& operator<<(ostream&o,vector<vector<T>>&vv){if(vv.size())o<<join(vv);return o;}
template<typename T1,typename T2>istream& operator>>(istream&i,pair<T1,T2>&v){return i>>v.first>>v.second;}
template<typename T1,typename T2>ostream& operator<<(ostream&o,pair<T1,T2>&v){return o<<"("<<v.first<<","<<v.second<<")";}
template<typename T>istream& operator>>(istream&i,array<T,2>&v){i>>v[0]>>v[1]; return i;}
template<typename T>ostream& operator<<(ostream&o,array<T,2>&v){return o<<"("<<v[0]<<","<<v[1]<<")"; return o;}

#define DEBUG(x);

#ifdef _DEBUG
#define DEBUG(x) std::cerr << #x << " : " << (x) << std::endl;
#define GLIBCXX_DEBUG
#define GLIBCXX_DEBUG_PEDANTIC
#endif
// _DEBUG

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

void init_init_init() {ios_base::sync_with_stdio(false);cin.tie(NULL);std::cout<<fixed<<setprecision(10);}
template<class T>T up(T a, T b){assert(b);return (a+b-1)/b;}
template<typename... A>bool eq(A const&... a){auto t={a...};assert(t.size());auto tar=*t.begin();for(const auto&e:t)if(tar!=e)return false;return true;}

template<class T>bool chmin(T &a, T b){if(a>b){a=b;return true;}return false;}
template<class T>bool chmax(T &a, T b){if(a<b){a=b;return true;}return false;}
template<class T>bool chmax(T &a, initializer_list<T>l){return chmax(a,max(l));}
template<class T>bool chmin(T &a, initializer_list<T>l){return chmin(a,min(l));}

//////////////////////////////////////////////////////////////////
// My Library
//////////////////////////////////////////////////////////////////

template<typename T> class Dinic {
private:
	int V;
	vector<int> level,iter;
	void bfs(int s) {
		fill(level.begin(),level.end(),-1);
		queue<int> que;
		level[s] = 0;
		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){
					level[e.to] = level[v] + 1;
					que.push(e.to);
				}
			}
		}
	}
	T dfs(int v,int t,T f) {
		if(v==t){
			return f;
		}
		for(int& i = iter[v]; i < (int)G[v].size(); i++){
			edge& e = G[v][i];
			if(e.cap > 0 && 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;
	}
     
public:
	struct edge{
		int to;
		T cap;
		int rev;
	};
	vector<vector<edge> > G;
     
	Dinic(int node_size) : V(node_size), level(V), iter(V), G(V){}
	// Adding edge on a node.
	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,(T)0,(int)G[from].size()-1});
	}
	// Calculate the maximum flow.
	T solve(int s,int t) {
		T flow = 0;
		for(;;){
			bfs(s);
			if(level[t] < 0) return flow;
			fill(iter.begin(),iter.end(),0);
			T f;
			while((f=dfs(s,t,numeric_limits<T>::max())) > 0){
				flow += f;
			}
		}
	}
};


//////////////////////////////////////////////////////////////////
// Contest Code
//////////////////////////////////////////////////////////////////



int main(int argc, char **argv)
{
	init_init_init();

	ll N; cin >> N;
	vector<string> S(N);
	REP(i, N) cin >> S[i];
	map<char, ll> is_last;
	for (auto e : S)
			is_last[e.back()] = true;
	for (char c = 'a'; c <= 'z'; ++c)
	{
			const ll start = 26;
			const ll goal = 27;
			Dinic<ll> max_flow(28);
			ll cnt{0};
			ll cnt2{0};
			REP(i, N)
			{
					string s = S[i];
					ll v1, v2;
					if (s.front() == c)
					{
							v1 = start;
							++cnt;
					}
					else
							v1 = (ll)(s.front() - 'a');
					if (s.back() == c)
					{
							v2 = goal;
							++cnt2;
					}
					else
							v2 = (ll)(s.back() - 'a');

					max_flow.add_edge(v1, v2, 1);
			}

			ll tmp = max_flow.solve(start, goal);
			if (cnt2 and cnt == tmp)
					std::cout << c << std::endl;
	}
	
}
accuracy: 1
time_ms: 20
memory_kb: 5416
final_rank: 7

code_snippet: //////////////////////////////
// Check before you submit.
// #define _ATCODER_LIBRARY

const long long MOD = 1e9+7;
// const long long MOD = 998244353;

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

#ifdef _ATCODER_LIBRARY
#include <atcoder/all>
using namespace atcoder;
#endif
// _ATCODER_LIBRARY

const long long INF = 1LL << 60;
const double PI = acos(-1);
using ll = long long;
using P = array<ll, 2>;

#define FOR(i,a,b) for (ll i=(a);i<(ll)(b);++i)
#define REP(i,n) FOR(i,0,n)
#define ALL(v) (v).begin(),(v).end()
#define SUM(v) accumulate(ALL(v),0ll)

template<typename T>istream& operator>>(istream&i,vector<T>&v){REP(j,v.size())i>>v[j];return i;}
template<typename T>string join(vector<T>&v){stringstream s;REP(i,v.size())s<<' '<<v[i];return s.str().substr(1);}
template<typename T>ostream& operator<<(ostream&o,vector<T>&v){if(v.size())o<<join(v);return o;}
template<typename T>string join(vector<vector<T>>&vv){string s="\n";REP(i,vv.size())s+=join(vv[i])+"\n";return s;}
template<typename T>ostream& operator<<(ostream&o,vector<vector<T>>&vv){if(vv.size())o<<join(vv);return o;}
template<typename T1,typename T2>istream& operator>>(istream&i,pair<T1,T2>&v){return i>>v.first>>v.second;}
template<typename T1,typename T2>ostream& operator<<(ostream&o,pair<T1,T2>&v){return o<<"("<<v.first<<","<<v.second<<")";}
template<typename T>istream& operator>>(istream&i,array<T,2>&v){i>>v[0]>>v[1]; return i;}
template<typename T>ostream& operator<<(ostream&o,array<T,2>&v){return o<<"("<<v[0]<<","<<v[1]<<")"; return o;}

#define DEBUG(x);

#ifdef _DEBUG
#define DEBUG(x) std::cerr << #x << " : " << (x) << std::endl;
#define GLIBCXX_DEBUG
#define GLIBCXX_DEBUG_PEDANTIC
#endif
// _DEBUG

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

void init_init_init() {ios_base::sync_with_stdio(false);cin.tie(NULL);std::cout<<fixed<<setprecision(10);}
template<class T>T up(T a, T b){assert(b);return (a+b-1)/b;}
template<typename... A>bool eq(A const&... a){auto t={a...};assert(t.size());auto tar=*t.begin();for(const auto&e:t)if(tar!=e)return false;return true;}

template<class T>bool chmin(T &a, T b){if(a>b){a=b;return true;}return false;}
template<class T>bool chmax(T &a, T b){if(a<b){a=b;return true;}return false;}
template<class T>bool chmax(T &a, initializer_list<T>l){return chmax(a,max(l));}
template<class T>bool chmin(T &a, initializer_list<T>l){return chmin(a,min(l));}

//////////////////////////////////////////////////////////////////
// My Library
//////////////////////////////////////////////////////////////////

template<typename T> class Dinic {
private:
	int V;
	vector<int> level,iter;
	void bfs(int s) {
		fill(level.begin(),level.end(),-1);
		queue<int> que;
		level[s] = 0;
		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){
					level[e.to] = level[v] + 1;
					que.push(e.to);
				}
			}
		}
	}
	T dfs(int v,int t,T f) {
		if(v==t){
			return f;
		}
		for(int& i = iter[v]; i < (int)G[v].size(); i++){
			edge& e = G[v][i];
			if(e.cap > 0 && 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;
	}
     
public:
	struct edge{
		int to;
		T cap;
		int rev;
	};
	vector<vector<edge> > G;
     
	Dinic(int node_size) : V(node_size), level(V), iter(V), G(V){}
	// Adding edge on a node.
	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,(T)0,(int)G[from].size()-1});
	}
	// Calculate the maximum flow.
	T solve(int s,int t) {
		T flow = 0;
		for(;;){
			bfs(s);
			if(level[t] < 0) return flow;
			fill(iter.begin(),iter.end(),0);
			T f;
			while((f=dfs(s,t,numeric_limits<T>::max())) > 0){
				flow += f;
			}
		}
	}
};


//////////////////////////////////////////////////////////////////
// Contest Code
//////////////////////////////////////////////////////////////////



int main(int argc, char **argv)
{
	init_init_init();

	ll N; cin >> N;
	vector<string> S(N);
	REP(i, N) cin >> S[i];
	map<char, ll> is_last;
	for (auto e : S)
			is_last[e.back()] = true;
	for (char c = 'a'; c <= 'z'; ++c)
	{
			const ll start = 26;
			const ll goal = 27;
			Dinic<ll> max_flow(28);
			ll cnt{0};
			ll cnt2{0};
			REP(i, N)
			{
					string s = S[i];
					ll v1, v2;
					if (s.front() == c)
					{
							v1 = start;
							++cnt;
					}
					else
							v1 = (ll)(s.front() - 'a');
					if (s.back() == c)
					{
							v2 = goal;
							++cnt2;
					}
					else
							v2 = (ll)(s.back() - 'a');

					max_flow.add_edge(v1, v2, 1);
			}

			ll tmp = max_flow.solve(start, goal);
			if (cnt2 and tmp == cnt)
					std::cout << c << std::endl;
	}
	
}
accuracy: 1
time_ms: 20
memory_kb: 5368
final_rank: 6

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

#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;
#define int long long
#define REP(i, n) for (int i = 0; i < (int)n; ++i)
#define RREP(i, n) for (int i = (int)n - 1; i >= 0; --i)
#define FOR(i, s, n) for (int i = s; i < (int)n; ++i)
#define RFOR(i, s, n) for (int i = (int)n - 1; i >= s; --i)
#define ALL(a) a.begin(), a.end()
#define IN(a, x, b) (a <= x && x < b)
template<class T>istream&operator >>(istream&is,vector<T>&vec){for(T&x:vec)is>>x;return is;}
template<class T>inline void out(T t){cout << t << "\n";}
template<class T,class... Ts>inline void out(T t,Ts... ts){cout << t << " ";out(ts...);}
template<class T>inline bool CHMIN(T&a,T b){if(a > b){a = b;return true;}return false;}
template<class T>inline bool CHMAX(T&a,T b){if(a < b){a = b;return true;}return false;}
constexpr int INF = 1e18;



signed main(){
	int N;
	cin >> N;
	vector<string>s(N);
	cin >> s;
	REP(i, 26) {
		mf_graph<int>graph(28);
		int S = 26, T = 27;
		int degOut = 0, degIn = 0;
		REP(j, N) {
			int u = s[j][0] - 'a';
			int v = s[j].back() - 'a';
			if(u == i) {
				u = S;
				++degOut;
			}
			if(v == i) {
				v = T;
				++degIn;
			}
			graph.add_edge(u, v, 1);
		}
		int f = graph.flow(S, T);
		if(f >= degOut && degIn > 0) out(char('a' + i));
	}
}
accuracy: 1
time_ms: 20
memory_kb: 5540
final_rank: 9

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 = 0xccccccc;
const ll LINF = 922337203685477580LL;
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 edge{
  int to, rev;
  int64_t cap;
  edge(int t=0, int64_t c=0, int r=0):to(t), cap(c), rev(r) {}
};

struct max_flow {
  vector<vector<edge> > G;
  vector<int> level, iter;
  int n;
  max_flow(int n=0):n(n), G(n), level(n), iter(n) {}
  inline void add(int from, int to, int64_t cap) {
    G[from].emplace_back(to, cap, G[to].size());
    G[to].emplace_back(from, 0, G[from].size()-1);
  }
  void bfs(int s) {
    for(int i = 0; i < n; i++) level[i] = -1;
    queue<int> que;
    level[s] = 0;
    que.emplace(s);
    while(!que.empty()) {
      int now = que.front();
      que.pop();
      for(edge &e:G[now]) {
        if(e.cap > 0 and level[e.to] < 0) {
          level[e.to] = level[now] + 1;
          que.emplace(e.to);
        }
      }
    }
  }
  int64_t dfs(int v, int t, int64_t f) {
    if(v == t) return f;
    for(int &i = iter[v]; i < G[v].size(); i++) {
      edge &e = G[v][i];
      if(e.cap > 0 and level[v] < level[e.to]) {
        int64_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;
  }
  int64_t Dinic(int s, int t) {
    int64_t flow = 0;
    while(true) {
      bfs(s);
      if(level[t] < 0) return flow;
      for(int i = 0; i < n; i++) iter[i] = 0;
      int64_t f;
      while((f = dfs(s, t, INT64_MAX)) > 0) flow += f;
    }
  }
};
#define N 10010

//head

int n;
string s[N];

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  cin >> n;
  rep(i, n) cin >> s[i];
  rep(i, 26) {
    max_flow mfl(28);
    const int ss = 27, tt = 26;
    int cnt = 0;
    bool back = false;
    rep(j, n) {
      int from = s[j][0]-'a', to = s[j][s[j].size()-1]-'a';
      if(from == i) {from = ss; cnt++;}
      if(to == i) {to = tt; back = true;}
      mfl.add(from, to, 1);
    }
    if(mfl.Dinic(ss, tt) == cnt and back) cout << char('a'+i) << '\n';
  }
}
accuracy: 1
time_ms: 10
memory_kb: 4984
final_rank: 3

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;
    cin >> N;
    string S[N];
    rep(i, N) cin >> S[i];
    rep(i, 26){
        Max_Flow<int> G(28);
        int s = 26, t = 27;
        int in = 0, out = 0;
        rep(j, N){
            int u = S[j].front()-'a', v = S[j].back()-'a';
            if(u == i) u = s, out++;
            if(v == i) v = t, in++;
            G.add_edge(u, v, 1);
        }
        if(G.max_flow(s, t) == out && in) cout << char('a'+i) << endl;
    }
}
accuracy: 1
time_ms: 70
memory_kb: 5248
final_rank: 11

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define INF 1000000000

struct max_flow
{
    int V;
    struct edge{int to,cap,rev;};
    using Graph=vector<vector<edge>>;
    Graph graph;
    vector<bool> visit;
public:
    max_flow(int n) //与えられた頂点は別にスタートとシンクを設定する場合
    {
        V=n;
        graph.resize(V+2,vector<edge> ());
        visit.resize(V+2,false);
    }

    void add_edge(int from,int to, int cap)
    {
        graph[from].pb((edge){to,cap,(int)graph[to].size()});
        graph[to].pb((edge){from,0,(int)graph[from].size()-1});
        return ;
    }

    int dfs(int s, int t, int f)
    {
        if (s==t) return f;
        visit[s]=true;
        for (int i=0;i<graph[s].size();i++)
        {
            edge &e=graph[s][i];
            if (!visit[e.to] && e.cap>0)
            {
                int d=dfs(e.to,t,min(f,e.cap));
                if (d>0)
                {
                    e.cap-=d;
                    graph[e.to][e.rev].cap+=d;
                    return d;
                }
            }
        }
        return 0;
    }

    int run(int s,int t)
    {
        int flow=0;
        for (;;) //while文
        {
            for (int i=0;i<V+2;i++) visit[i]=false;//memsetは初期化
            int f=dfs(s,t,INF);
            if (f==0) return flow;
            else flow+=f;
        }
    }
};

int main()
{
    int N; cin >> N;
    int cnt[28][28];string input;
    for (int i=0;i<27;i++)
        for (int j=0;j<27;j++) cnt[i][j]=0;
    for (int i=0;i<N;i++)
    {
        cin >> input;
        cnt[(int)input[0]-'a'][(int)input[input.size()-1]-'a']++;
    }
    vector<char> ans;
    for (int s=0;s<26;s++)
    {
        max_flow solve(26);
        for (int i=0;i<26;i++)
        {
            if (i!=s)
            {
                for (int j=0;j<26;j++)
                {
                    if (j!=s && cnt[i][j]>0 &&i!=j) solve.add_edge(i+1,j+1,cnt[i][j]);
                }
            }
            else continue;
        }
        int sum=0;
        bool ok=false;
        if (cnt[s][s]>0) {ok=true;solve.add_edge(0,27,cnt[s][s]);}
        for (int i=0;i<26;i++)
        {
            sum+=cnt[s][i];
            if (cnt[s][i]>0 && i!=s ) solve.add_edge(0,i+1,cnt[s][i]);
            if (cnt[i][s]>0 && i!=s) {solve.add_edge(i+1,27,cnt[i][s]);ok=true;}
        }
        if (solve.run(0,27)==sum && ok)
        {
            char a='a'+s;
            ans.pb(a);
        }
    }
    for (int i=0;i<ans.size();i++) cout << ans[i] << endl;
return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 3232
final_rank: 1

code_snippet: #include<bits/stdc++.h>
#define enld '\n'
#define rep(i,n) for(int i=0; i<(n); i++)
using namespace std;
using ll = long long;
using P = pair<int,int>;
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
#pragma GCC optimize("Ofast")
constexpr ll INF = 1e18;
constexpr int inf = 1e9;
constexpr double INFD = 1e100;
constexpr ll mod = 1000000007;
constexpr ll mod2 = 998244353;
const double PI = 3.1415926535897932384626433832795028841971;
const int dx[4] = {1, 0, -1, 0};
const int dy[4] = {0, 1, 0, -1};
template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return 1; } return 0; }
template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return 1; } return 0; }
// ios::sync_with_stdio(false);
// cin.tie(nullptr);
// ---------------------------------------------------------------------------


#define MAX_V 1000000
struct edge{
    int to,cap,rev;
};

vector<vector<edge>> G(MAX_V);
bool used[MAX_V];

void add_edge(int from,int to,int cap){
    edge F;
    edge T;
    F.to = to;
    F.cap = cap;
    F.rev = G[to].size();
    G[from].push_back(F);

    T.to = from;
    T.cap = 0;
    T.rev = G[from].size()-1;
    G[to].push_back(T);
}

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] and 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;
    while(1){
        memset(used, 0, sizeof(used));
        int f = dfs(s,t,inf);
        if(f == 0) return flow;
        flow += f;
    }
}


int main(){
    int N;
    cin >> N;
    vector<string> S(N);
    for(int i=0; i<N; i++){
        cin >> S[i];
    }
    vector<bool> last(26,false);
    for(int i=0; i<N; i++){
        last[S[i].back()-'a'] = true;
    }
    for(int c=0; c<26; c++){
        if(!last[c]) continue;
        int s=26,t=27;
        int cnt = 0;
        memset(used,0,sizeof(used));
        for(int i=0; i<30; i++){
            G[i].clear();
        }
        for(int i=0; i<N; i++){
            int u = (S[i].front() == c+'a') ? s : (S[i].front()-'a');
            int v = (S[i].back() == c+'a') ? t : (S[i].back()-'a');
            // cout << u << " " << v << enld;
            add_edge(u,v,1);
            if(S[i].front() == c+'a') cnt++; 
        }
        // cout << G[s].size() << enld;
        int flow = max_flow(s,t);
        // cout << "check:" << char(c+'a') << " " << flow << " " << cnt << enld;
        if(flow == cnt){
            cout << char(c+'a') << enld;
        }
    }
    return 0;
}
accuracy: 1
time_ms: 350
memory_kb: 29584
final_rank: 17

code_snippet: #include <algorithm>
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

template<typename flow_t>
struct Dinic {
    struct Edge {
        int to; flow_t cap; int rev; bool isrev;
        Edge(int t, flow_t c, int r, bool b) : to(t), cap(c), rev(r), isrev(b) { }
    };
    const flow_t INF;
    vector<vector<Edge>> g;
    vector<int> min_cost, iter;
    Dinic(int n) : INF(numeric_limits<flow_t>::max()), g(n) { }
    void add_edge(int from, int to, flow_t cap) {
        g[from].emplace_back(Edge(to, cap, g[to].size(), false));
        g[to].emplace_back(Edge(from, 0, g[from].size() - 1, true));
    }
    bool bfs(int s, int t) {
        min_cost.assign(g.size(), -1); min_cost[s] = 0;
        queue<int> que; que.emplace(s);
        while (!que.empty() && min_cost[t] == -1) {
            int v = que.front(); que.pop();
            for (auto &e: g[v]) if (e.cap > 0 && min_cost[e.to] == -1) {
                min_cost[e.to] = min_cost[v] + 1;
                que.emplace(e.to);
            }
        }
        return min_cost[t] != -1;
    }
    flow_t dfs(int v, int t, flow_t flow) {
        if (v == t) return flow;
        for (int &i = iter[v]; i < (int)g[v].size(); i++) {
            Edge &e = g[v][i];
            if (e.cap > 0 && min_cost[v] < min_cost[e.to]) {
                flow_t d = dfs(e.to, t, min(flow, e.cap));
                if (d > 0) {
                    e.cap -= d, g[e.to][e.rev].cap += d;
                    return d;
                }
            }
        }
        return 0;
    }
    flow_t max_flow(int s, int t) {
        flow_t flow = 0;
        while (bfs(s, t)) {
            iter.assign(g.size(), 0);
            flow_t f = 0;
            while ((f = dfs(s, t, INF)) > 0) flow += f;
        }
        return flow;
    }
    friend ostream& operator<<(ostream& os, Dinic& mf) {
        for (int i = 0; i < (int)mf.g.size(); i++) {
            for (auto &e: mf.g[i]) if (!e.isrev) {
                auto &rev_e = mf.g[e.to][e.rev];
                os << i << " -> " << e.to << " (flow: ";
                os << rev_e.cap << " / " << e.cap + rev_e.cap << ")" << endl;
            }
        }
        return os;
    }
};

int main() {
    int n; cin >> n;
    vector<string> ss(n);
    for (auto &si: ss) cin >> si;
    string ans;
    for (int c = 0; c < 26; c++) {
        Dinic<int> mf(26 + 2);
        int s = 26, t = s + 1;
        int deg_out = 0, deg_in = 0;
        for (auto &si: ss) {
            int u = si.front() - 'a', v = si.back() - 'a';
            if (u == c) u = s, deg_out++;
            if (v == c) v = t, deg_in++;
            mf.add_edge(u, v, 1);
        }
        if (deg_in && mf.max_flow(s, t) == deg_out) ans += 'a' + c;
    }
    for (char c: ans) cout << c << endl;
    return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 5360
final_rank: 10

code_snippet: #include <algorithm>
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

template<typename flow_t>
struct Dinic {
    struct Edge {
        int to; flow_t cap; int rev; bool isrev;
        Edge(int t, flow_t c, int r, bool b) : to(t), cap(c), rev(r), isrev(b) { }
    };
    const flow_t INF;
    vector<vector<Edge>> g;
    vector<int> min_cost, iter;
    Dinic(int n) : INF(numeric_limits<flow_t>::max()), g(n) { }
    void add_edge(int from, int to, flow_t cap) {
        g[from].emplace_back(Edge(to, cap, g[to].size(), false));
        g[to].emplace_back(Edge(from, 0, g[from].size() - 1, true));
    }
    bool bfs(int s, int t) {
        min_cost.assign(g.size(), -1); min_cost[s] = 0;
        queue<int> que; que.emplace(s);
        while (!que.empty() && min_cost[t] == -1) {
            int v = que.front(); que.pop();
            for (auto &e: g[v]) if (e.cap > 0 && min_cost[e.to] == -1) {
                min_cost[e.to] = min_cost[v] + 1;
                que.emplace(e.to);
            }
        }
        return min_cost[t] != -1;
    }
    flow_t dfs(int v, int t, flow_t flow) {
        if (v == t) return flow;
        for (int i = iter[v]; i < (int)g[v].size(); i++) {
            Edge &e = g[v][i];
            if (e.cap > 0 && min_cost[v] < min_cost[e.to]) {
                flow_t d = dfs(e.to, t, min(flow, e.cap));
                if (d > 0) {
                    e.cap -= d, g[e.to][e.rev].cap += d;
                    return d;
                }
            }
        }
        return 0;
    }
    flow_t max_flow(int s, int t) {
        flow_t flow = 0;
        while (bfs(s, t)) {
            iter.assign(g.size(), 0);
            flow_t f = 0;
            while ((f = dfs(s, t, INF)) > 0) flow += f;
        }
        return flow;
    }
};

int main() {
    int n; cin >> n;
    vector<string> ss(n);
    for (auto &si: ss) cin >> si;
    string ans;
    for (int c = 0; c < 26; c++) {
        Dinic<int> mf(26 + 2);
        int s = 26, t = s + 1;
        int deg_out = 0, deg_in = 0;
        for (auto &si: ss) {
            int u = si.front() - 'a', v = si.back() - 'a';
            if (u == c) u = s, deg_out++;
            if (v == c) v = t, deg_in++;
            mf.add_edge(u, v, 1);
        }
        if (deg_in && mf.max_flow(s, t) == deg_out) ans += 'a' + c;
    }
    for (char c: ans) cout << c << endl;
    return 0;
}
accuracy: 1
time_ms: 930
memory_kb: 5320
final_rank: 14

code_snippet: #include <algorithm>
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

template<typename flow_t>
struct Dinic {
    struct Edge {
        int to; flow_t cap; int rev; bool isrev;
        Edge(int t, flow_t c, int r, bool b) : to(t), cap(c), rev(r), isrev(b) { }
    };
    const flow_t INF;
    vector<vector<Edge>> g;
    vector<int> min_cost, iter;
    Dinic(int n) : INF(numeric_limits<flow_t>::max()), g(n) { }
    void add_edge(int from, int to, flow_t cap) {
        g[from].emplace_back(Edge(to, cap, g[to].size(), false));
        g[to].emplace_back(Edge(from, 0, g[from].size() - 1, true));
    }
    bool bfs(int s, int t) {
        min_cost.assign(g.size(), -1); min_cost[s] = 0;
        queue<int> que; que.emplace(s);
        while (!que.empty() && min_cost[t] == -1) {
            int v = que.front(); que.pop();
            for (auto &e: g[v]) if (e.cap > 0 && min_cost[e.to] == -1) {
                min_cost[e.to] = min_cost[v] + 1;
                que.emplace(e.to);
            }
        }
        return min_cost[t] != -1;
    }
    flow_t dfs(int idx, int t, flow_t flow) {
        if (idx == t) return flow;
        for (int i = iter[idx]; i < (int)g[idx].size(); i++) {
            Edge &e = g[idx][i];
            if (e.cap > 0 && min_cost[idx] < min_cost[e.to]) {
                flow_t d = dfs(e.to, t, min(flow, e.cap));
                if (d > 0) {
                    e.cap -= d, g[e.to][e.rev].cap += d;
                    return d;
                }
            }
        }
        return 0;
    }
    flow_t max_flow(int s, int t) {
        flow_t flow = 0;
        while (bfs(s, t)) {
            iter.assign(g.size(), 0);
            flow_t f = 0;
            while ((f = dfs(s, t, INF)) > 0) flow += f;
        }
        return flow;
    }
    friend ostream& operator<<(ostream& os, Dinic& mf) {
        for (int i = 0; i < (int)mf.g.size(); i++) {
            for (auto &e: mf.g[i]) if (!e.isrev) {
                auto &rev_e = mf.g[e.to][e.rev];
                os << i << " -> " << e.to << " (flow: ";
                os << rev_e.cap << " / " << e.cap + rev_e.cap << ")" << endl;
            }
        }
        return os;
    }
};

int main() {
    int n; cin >> n;
    vector<string> ss(n);
    for (auto &si: ss) cin >> si;
    for (int c = 0; c < 26; c++) {
        Dinic<int> mf(26 + 2);
        int s = 26, t = s + 1;
        int deg_out = 0, deg_in = 0;
        for (auto &si: ss) {
            int u = si.front() - 'a', v = si.back() - 'a';
            if (u == c) u = s, deg_out++;
            if (v == c) v = t, deg_in++;
            mf.add_edge(u, v, 1);
        }
        if (deg_in && mf.max_flow(s, t) == deg_out) cout << char('a' + c) << endl;
    }
    return 0;
}
accuracy: 1
time_ms: 930
memory_kb: 5336
final_rank: 15

code_snippet: #include <algorithm>
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

template<typename flow_t>
struct Dinic {
    struct Edge {
        int to; flow_t cap; int rev; bool isrev;
        Edge(int t, flow_t c, int r, bool b) : to(t), cap(c), rev(r), isrev(b) { }
    };
    const flow_t INF;
    vector<vector<Edge>> g;
    vector<int> min_cost, iter;
    Dinic(int n) : INF(numeric_limits<flow_t>::max()), g(n), min_cost(n) { }
    void add_edge(int from, int to, flow_t cap) {
        g[from].emplace_back(Edge(to, cap, g[to].size(), false));
        g[to].emplace_back(Edge(from, 0, g[from].size() - 1, true));
    }
    bool bfs(int s, int t) {
        fill(begin(min_cost), end(min_cost), -1), min_cost[s] = 0;
        queue<int> que; que.emplace(s);
        while (!que.empty() && min_cost[t] == -1) {
            int v = que.front(); que.pop();
            for (auto &e: g[v]) if (e.cap > 0 && min_cost[e.to] == -1) {
                min_cost[e.to] = min_cost[v] + 1;
                que.emplace(e.to);
            }
        }
        return min_cost[t] != -1;
    }
    flow_t dfs(int idx, int t, flow_t flow) {
        if (idx == t) return flow;
        for (int i = iter[idx]; i < (int)g[idx].size(); i++) {
            Edge &e = g[idx][i];
            if (e.cap > 0 && min_cost[idx] < min_cost[e.to]) {
                flow_t d = dfs(e.to, t, min(flow, e.cap));
                if (d > 0) {
                    e.cap -= d, g[e.to][e.rev].cap += d;
                    return d;
                }
            }
        }
        return 0;
    }
    flow_t max_flow(int s, int t) {
        flow_t flow = 0;
        while (bfs(s, t)) {
            iter.assign(g.size(), 0);
            flow_t f = 0;
            while ((f = dfs(s, t, INF)) > 0) flow += f;
        }
        return flow;
    }
    friend ostream& operator<<(ostream& os, Dinic& mf) {
        for (int i = 0; i < (int)mf.g.size(); i++) {
            for (auto &e: mf.g[i]) if (!e.isrev) {
                auto &rev_e = mf.g[e.to][e.rev];
                os << i << " -> " << e.to << " (flow: ";
                os << rev_e.cap << " / " << e.cap + rev_e.cap << ")" << endl;
            }
        }
        return os;
    }
};

int main() {
    int n; cin >> n;
    vector<string> ss(n);
    for (auto &si: ss) cin >> si;
    string ans;
    for (int c = 0; c < 26; c++) {
        Dinic<int> mf(28);
        int s = 26, t = s + 1;
        int deg_out = 0, deg_in = 0;
        for (auto &si: ss) {
            int u = si.front() - 'a', v = si.back() - 'a';
            if (u == c) u = s, deg_out++;
            if (v == c) v = t, deg_in++;
            mf.add_edge(u, v, 1);
        }
        if (deg_in && mf.max_flow(s, t) == deg_out) ans += 'a' + c;
    }
    for (char c: ans) cout << c << endl;
    return 0;
}
accuracy: 1
time_ms: 950
memory_kb: 5348
final_rank: 16

code_snippet: #include <algorithm>
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

template<typename flow_t>
struct Dinic {
    struct Edge {
        int to; flow_t cap; int rev; bool isrev;
        Edge(int t, flow_t c, int r, bool b) : to(t), cap(c), rev(r), isrev(b) { }
    };
    const flow_t INF;
    vector<vector<Edge>> g;
    vector<int> min_cost, iter;
    Dinic(int n) : INF(numeric_limits<flow_t>::max()), g(n) { }
    void add_edge(int from, int to, flow_t cap) {
        g[from].emplace_back(Edge(to, cap, g[to].size(), false));
        g[to].emplace_back(Edge(from, 0, g[from].size() - 1, true));
    }
    bool bfs(int s, int t) {
        min_cost.assign(g.size(), -1); min_cost[s] = 0;
        queue<int> que; que.emplace(s);
        while (!que.empty() && min_cost[t] == -1) {
            int v = que.front(); que.pop();
            for (auto &e: g[v]) if (e.cap > 0 && min_cost[e.to] == -1) {
                min_cost[e.to] = min_cost[v] + 1;
                que.emplace(e.to);
            }
        }
        return min_cost[t] != -1;
    }
    flow_t dfs(int idx, int t, flow_t flow) {
        if (idx == t) return flow;
        for (int i = iter[idx]; i < (int)g[idx].size(); i++) {
            Edge &e = g[idx][i];
            if (e.cap > 0 && min_cost[idx] < min_cost[e.to]) {
                flow_t d = dfs(e.to, t, min(flow, e.cap));
                if (d > 0) {
                    e.cap -= d, g[e.to][e.rev].cap += d;
                    return d;
                }
            }
        }
        return 0;
    }
    flow_t max_flow(int s, int t) {
        flow_t flow = 0;
        while (bfs(s, t)) {
            iter.assign(g.size(), 0);
            flow_t f = 0;
            while ((f = dfs(s, t, INF)) > 0) flow += f;
        }
        return flow;
    }
    friend ostream& operator<<(ostream& os, Dinic& mf) {
        for (int i = 0; i < (int)mf.g.size(); i++) {
            for (auto &e: mf.g[i]) if (!e.isrev) {
                auto &rev_e = mf.g[e.to][e.rev];
                os << i << " -> " << e.to << " (flow: ";
                os << rev_e.cap << " / " << e.cap + rev_e.cap << ")" << endl;
            }
        }
        return os;
    }
};

int main() {
    int n; cin >> n;
    vector<string> ss(n);
    for (auto &si: ss) cin >> si;
    string ans;
    for (int c = 0; c < 26; c++) {
        Dinic<int> mf(28);
        int s = 26, t = s + 1;
        int deg_out = 0, deg_in = 0;
        for (auto &si: ss) {
            int u = si.front() - 'a', v = si.back() - 'a';
            if (u == c) u = s, deg_out++;
            if (v == c) v = t, deg_in++;
            mf.add_edge(u, v, 1);
        }
        if (deg_in && mf.max_flow(s, t) == deg_out) ans += 'a' + c;
    }
    for (char c: ans) cout << c << endl;
    return 0;
}
accuracy: 1
time_ms: 920
memory_kb: 5412
final_rank: 13