Problem:
まるかいて
太郎君は小学生で、チラシの裏に落書きをしています。

ある時、太郎君は次のゲームを思いつきました。
n×n
の格子状のマス目を書いておきます。
それぞれのマス目の初期状態は、丸印が書かれているか、書かれていないかのどちらか一方です。
これらの丸印を消したり書いたりして最終的にどの一列を見ても必ずちょうど1つのみの丸印が、どの一行を見ても必ず1つのみの丸印が存在するようにすることが目標であり、この状態にすればゲームをクリアしたことになります。
太郎君はこのゲームを思いつきましたが、太郎君はこのゲームをクリアするのに大変時間がかかってしまいます。そこで、大学生であるあなたに助けを求めました。

太郎君の兄であり大学生であるあなたの仕事は以下の通りです。
厳密な状況を考えるために、あるマス目に丸印を書き込むコスト、あるマス目にある丸印を消すコストをあなたは導き出しました。このコストを用いてこのゲームをクリアするためにかかる操作のコストを最小化するような手順を考える。
このとき、最小のコストおよびそのコストを達成するような手順を出力するプログラムを書いてください。
出力については、最小コストを達成する手順なら、どのような操作、順番でも出力してもよいものとする。
Input
n
W
11
W
12
..
W
1n
W
21
W
22
..
W
2n
..
W
n1
W
n2
..
W
nn
E
11
E
12
..
E
1n
E
21
E
22
..
E
2n
..
E
n1
E
n2
..
E
nn
F
1
(
n
文字)
F
2
(
n
文字)
..
F
n
(
n
文字)
n
は太郎君の作ったマス目が一辺にいくつあるかを表す
W
ij
は上から
i
番目、左から
j
番目のマス目に丸印を書き込むコストを表す
E
ij
は上から
i
番目、左から
j
番目のマス目に書かれてある丸印を消すコストを表す
F
i
は上から
i
番目の行のマス目の初期状態を表す
F
i
の左から
j
文字目について
'o'のとき、上から
i
番目、左から
j
番目のマス目に丸印が書かれてあることを表す。
'.'のとき、上から
i
番目、左から
j
番目のマス目が空白であることを表す。
Constraints
1≤ n ≤ 100
1≤ W
ij
≤ 1000
1≤ E
ij
≤ 1000
F
i
は文字列であり、その長さは
n
である
F
i
は'o'と'.'のみで構成されている
Output
mincost
cnt
R
1
C
1
operate
1
R
2
C
2
operate
2
..
R
cnt
C
cnt
operate
cnt
mincost
は、太郎君のゲームをクリアするために必要な最小コストを表す。
mincost
は書き込み操作、消去操作で発生するコストの総和で計算される。
cnt
:
mincost
のコストを達成する操作を行った回数を表す
k
回目
(1≤k≤cnt)
に実行する操作は
k+2
行目に記述する
k
回目
(1≤k≤cnt)
の操作に対して
上から
i
番目のマス目、左から
j
番目のマス目に対して行ったものとすると
R
k
k
である。
この操作が丸印を消す操作であるならば
operate
k
= "erase"とせよ
この操作が丸印を書き込む操作であるならば
operate
k
= "write"とせよ
R
k
,C
k
,operate
k
は一行に空白区切りで出力しなければならない
丸印の書かれてあるマス目に対して丸印を記述する操作、および丸印が書かれていないマス目に対して丸印を消去する操作をした場合はWrongAnswerである
cnt
個の操作にかかるコストの総和が
mincost
に一致しないときはWrongAnswerである
Sample Input 1
3
1 1 1
1 1 1
1 1 1
1 1 1 
1 1 1
1 1 1
o.o
...
.o.
Output for the Sample Input 1
2
2
1 3 erase
2 3 write
上から1番目、左から3番目のマス目の丸印を消去し、
上から2番目、左から3番目のマス目に丸印を書き加えれば目標は達成できる。
このときコストは2のみしかかからず、これが最小のコストである。
Sample Input 2
4
1 2 3 4 
1 2 3 4 
1 2 3 4 
1 2 3 4
1 2 3 4 
1 2 3 4 
1 2 3 4 
1 2 3 4
oooo
oooo
oooo
oooo
Output for the Sample Input 2
30
12
1 1 erase
1 2 erase
1 3 erase
2 1 erase
2 2 erase
2 4 erase
3 1 erase
3 3 erase
3 4 erase
4 2 erase
4 3 erase
4 4 erase
コスト(1+2+3+4)*3だけ消去処理をすればクリアとなります。
Sample Input 3
3
1 1 1
1 1 1
1 1 1
1 1 1
1 1 1
1 1 1
o..
.o.
..o
Output for the Sample Input 3
0
0
すでに目標は達成されているため、コスト及び操作回数はともに0となる。

Submitted solutions:

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

const int MAX_N = 100;  // Максимальное количество вершин
const int MAX_V = 1000; // Максимальное количество вершин в сети

struct Edge {
    int to, cap, cost, rev;
};

int n, N, K, V;
int W[MAX_N][MAX_N], E[MAX_N][MAX_N];
string F[MAX_N];
vector<Edge> G[MAX_V];  // Граф сети
int dist[MAX_V], prevv[MAX_V], preve[MAX_V]; // Для хранения пути

// Добавление ребра в граф
void add_edge(int from, int to, int cap, int cost) {
    G[from].push_back({to, cap, cost, (int)G[to].size()});
    G[to].push_back({from, 0, -cost, (int)G[from].size() - 1});
}

// Алгоритм поиска минимальной стоимости потока
int min_cost_flow(int s, int t, int f) {
    int total_cost = 0;
    while (f > 0) {
        fill(dist, dist + V, INF);
        dist[s] = 0;
        bool update = true;

        while (update) {
            update = false;
            for (int v = 0; v < V; ++v) {
                if (dist[v] == INF) continue;
                for (int i = 0; i < G[v].size(); ++i) {
                    Edge &e = G[v][i];
                    if (e.cap > 0 && dist[e.to] > dist[v] + e.cost) {
                        dist[e.to] = dist[v] + e.cost;
                        prevv[e.to] = v;
                        preve[e.to] = i;
                        update = true;
                    }
                }
            }
        }

        // Если нет пути до t, возвращаем -1
        if (dist[t] == INF) return -1;

        int d = f;
        for (int v = t; v != s; v = prevv[v]) {
            d = min(d, G[prevv[v]][preve[v]].cap);
        }

        f -= d;
        total_cost += d * dist[t];

        // Обновляем остаточные ёмкости
        for (int v = t; v != s; v = prevv[v]) {
            Edge &e = G[prevv[v]][preve[v]];
            e.cap -= d;
            G[v][e.rev].cap += d;
        }
    }
    return total_cost;
}

// Основная функция для двудольного сопоставления
int Bipartite_Matching() {
    int source = N + K, sink = source + 1;

    // Соединяем исход с вершинами первой доли
    for (int i = 0; i < N; ++i) add_edge(source, i, 1, 0);

    // Соединяем вершины второй доли с стоком
    for (int i = 0; i < K; ++i) add_edge(N + i, sink, 1, 0);

    int initial_cost = 0;

    // Добавляем рёбра между долями с соответствующими весами
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            if (F[i][j] == 'o') {
                initial_cost += E[i][j];
                add_edge(i, N + j, 1, -E[i][j]);  // Если 'o', это исходное состояние
            } else {
                add_edge(i, N + j, 1, W[i][j]);   // Если '.', это потенциальный переход
            }
        }
    }

    return initial_cost + min_cost_flow(source, sink, N);
}

int main() {
    // Ввод данных
    cin >> n;
    for (int i = 0; i < n; ++i)
        for (int j = 0; j < n; ++j)
            cin >> W[i][j];

    for (int i = 0; i < n; ++i)
        for (int j = 0; j < n; ++j)
            cin >> E[i][j];

    for (int i = 0; i < n; ++i)
        cin >> F[i];

    N = K = n;
    V = 2 * n + 2;

    // Решение задачи
    cout << Bipartite_Matching() << endl;

    int cnt = 0;

    // Подсчёт и вывод результата
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < (int)G[i].size(); ++j) {
            int to = G[i][j].to - N;
            if (G[i][j].cap <= 0 && F[i][to] == '.') cnt++;
            if (G[i][j].cap > 0 && F[i][to] == 'o') cnt++;
        }
    }

    cout << cnt << endl;

    // Вывод операций "write" и "erase"
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < (int)G[i].size(); ++j) {
            int to = G[i][j].to - N;
            if (G[i][j].cap <= 0 && F[i][to] == '.')
                cout << i + 1 << " " << to + 1 << " write" << endl;
            else if (G[i][j].cap > 0 && F[i][to] == 'o')
                cout << i + 1 << " " << to + 1 << " erase" << endl;
        }
    }

    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3932
final_rank: 9

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

// ========================================================================== Library Part ==========================================================================
struct Edge {
    int to, cap, rev, cost;
};

class MinCostFlow {
    public:
    int size_ = 0;
    vector<vector<Edge>> G;

    void init(int sz) {
        size_ = sz;
        G.resize(size_, vector<Edge>(0));
    }

    void add_edge(int u, int v, int c, int d) {
        G[u].push_back(Edge{v, c, (int)G[v].size() - 0, +d});
        G[v].push_back(Edge{u, 0, (int)G[u].size() - 1, -d});
    }

    int min_flow(int s, int t, int f) {
        int Answer = 0;

        // Simulation Start
        while (f > 0) {
            vector<int> Dist;
            vector<int> PreV;
            vector<int> PreE;
            Dist.resize(size_, 100000000);
            PreV.resize(size_, -1);
            PreE.resize(size_, -1);

            // Shortest Path
            Dist[s] = 0;
            for (int i = 0; i < size_; i++) {
                for (int j = 0; j < size_; j++) {
                    if (Dist[j] == 100000000) continue;
                    for (int k = 0; k < (int)G[j].size(); k++) {
                        if (G[j][k].cap == 0) continue;
                        int nex = G[j][k].to;
                        int cos = G[j][k].cost;
                        if (Dist[nex] > Dist[j] + cos) {
                            Dist[nex] = Dist[j] + cos;
                            PreV[nex] = j;
                            PreE[nex] = k;
                        }
                    }
                }
            }
            if (Dist[t] == 100000000) return -1;

            // Fukugen 1
            int cx = t, mincap = f;
            while (cx != s) {
                int nex = PreV[cx];
                int idx = PreE[cx];
                mincap = min(mincap, G[nex][idx].cap);
                cx = nex;
            }
            Answer += mincap * Dist[t];

            // Fukugen 2
            cx = t;
            while (cx != s) {
                int nex = PreV[cx];
                int idx = PreE[cx];
                G[nex][idx].cap -= mincap;
                G[G[nex][idx].to][G[nex][idx].rev].cap += mincap;
                cx = nex;
            }
            f -= mincap;
        }

        // Return
        return Answer;
    }
};

// ========================================================================== Main Part ==========================================================================
int N;
int Cost1[1009][1009];
int Cost2[1009][1009];
char C[1009][1009];
int Calc[1009][1009];

int main() {
    // Step 1. Input
    cin >> N;
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= N; j++) cin >> Cost1[i][j];
    }
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= N; j++) cin >> Cost2[i][j];
    }
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= N; j++) cin >> C[i][j];
    }

    // Step 2. Get Cost
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= N; j++) {
            for (int k = 1; k <= N; k++) {
                if (j == k && C[i][k] == '.') Calc[i][j] += Cost1[i][k];
                if (j != k && C[i][k] == 'o') Calc[i][j] += Cost2[i][k];
            }
        }
    }

    // Step 3. Get Graph
    MinCostFlow Z; Z.init(2 * N + 8);
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= N; j++) Z.add_edge(i, N + j, 1, Calc[i][j]);
    }
    for (int i = 1; i <= N; i++) Z.add_edge(2 * N + 1, i, 1, 0);
    for (int i = 1; i <= N; i++) Z.add_edge(N + i, 2 * N + 2, 1, 0);

    // Step 4. Flow
    int Answer = Z.min_flow(2 * N + 1, 2 * N + 2, N);
    vector<tuple<int, int, string>> Answer2;
    for (int i = 1; i <= N; i++) {
        for (int j = 0; j < Z.G[i].size(); j++) {
            int nex = Z.G[i][j].to;
            if (N + 1 <= nex && nex <= 2 * N && Z.G[i][j].cap == 0) {
                for (int k = 1; k <= N; k++) {
                    if (k == nex - N && C[i][k] == '.') Answer2.push_back(make_tuple(i, k, "write"));
                    if (k != nex - N && C[i][k] == 'o') Answer2.push_back(make_tuple(i, k, "erase"));
                }
            }
        }
    }

    // Step 5. Output
    cout << Answer << endl;
    cout << Answer2.size() << endl;
    for (int i = 0; i < (int)Answer2.size(); i++) {
        cout << get<0>(Answer2[i]) << " " << get<1>(Answer2[i]) << " " << get<2>(Answer2[i]) << endl;
    }
    return 0;
}
accuracy: 1
time_ms: 370
memory_kb: 10452
final_rank: 20

code_snippet: // 

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

using ll = long long;

#define rep(i, n) for (int i=0; i<(int)(n); ++(i))
#define rep3(i, m, n) for (int i=(m); (i)<(int)(n); ++(i))
#define repr(i, n) for (int i=(int)(n)-1; (i)>=0; --(i))
#define rep3r(i, m, n) for (int i=(int)(n)-1; (i)>=(int)(m); --(i))
#define all(x) (x).begin(), (x).end()

const int INF = (int)(1e9);

struct MinCostFlow {
    struct edge {
        int to, cap, rev, cost;
        edge(int t, int ca, int r, int co) : to(t), cap(ca), rev(r), cost(co) {}
    };
    int n;
    vector<vector<edge>> g;
    vector<int> dist, preve, prevv;
    MinCostFlow(int n_) {
        n = n_;
        g.resize(n, vector<edge>());
        dist.resize(n), preve.resize(n), prevv.resize(n);
    }
    void add_edge(int f, int t, int ca, int co) {
        g[f].emplace_back(t, ca, (int)(g[t].size()), co);
        g[t].emplace_back(f, 0, (int)(g[f].size())-1, -co);
    }
    int min_cost_flow(int s, int t, int f) {
        int res = 0;
        while (f > 0) {
            dist.assign(n, INF);
            dist[s] = 0;
            bool update = true;
            while (update) {
                update = false;
                rep(v, n) if (dist[v] != INF) {
                    rep(e, g[v].size()) if (g[v][e].cap>0 && dist[g[v][e].to]>dist[v]+g[v][e].cost) {
                        dist[g[v][e].to] = dist[v] + g[v][e].cost;
                        preve[g[v][e].to] = e;
                        prevv[g[v][e].to] = v;
                        if (!update) update = true;
                    }
                }
            }
            if (dist[t] == INF) return -1;
            int d = f;
            for (int v=t; v!=s; v=prevv[v]) d = min(d, g[prevv[v]][preve[v]].cap);
            f -= d;
            res += d * dist[t];
            for (int v=t; v!=s; v=prevv[v]) {
                g[prevv[v]][preve[v]].cap -= d;
                g[v][g[prevv[v]][preve[v]].rev].cap += d;
            }
        }
        return res;
    }
};

int main() {
	int n;
	cin >> n;
	vector<vector<int>> w(n, vector<int>(n)), e(n, vector<int>(n));
	rep(i, n) rep(j, n) cin >> w[i][j];
	rep(i, n) rep(j, n) cin >> e[i][j];
	vector<string> f(n);
	rep(i, n) cin >> f[i];
	MinCostFlow mcf(2*n+2);
	rep(i, n) {
		int scost = 0;
		rep(j, n) if (f[i][j] == 'o') scost += e[i][j];
		rep(j, n) {
			int tcost = scost + (f[i][j]=='o' ? -e[i][j] : w[i][j]);
			mcf.add_edge(i, n+j, 1, tcost);
		}
	}
	rep(i, n) mcf.add_edge(2*n, i, 1, 0);
	rep(i, n) mcf.add_edge(n+i, 2*n+1, 1, 0);
	int res = mcf.min_cost_flow(2*n, 2*n+1, n);
	cout << res << endl;
	vector<tuple<int, int, string>> rlst;
	rep(i, n) for (const auto& ei : mcf.g[i]) if (ei.cap==0 && ei.cost>0) {
		rep(j, n) {
			if (j!=ei.to-n && f[i][j]=='o') rlst.emplace_back(i+1, j+1, "erase");
			if (j==ei.to-n && f[i][j]=='.') rlst.emplace_back(i+1, j+1, "write"); 
		}
	}
	int rlen = rlst.size();
	cout << rlen << endl;
	rep(i, rlen) cout << get<0>(rlst[i]) << ' ' << get<1>(rlst[i]) << ' ' << get<2>(rlst[i]) << endl;
	return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 4028
final_rank: 11

code_snippet: // 

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

using ll = long long;

#define rep(i, n) for (int i=0; i<(int)(n); ++(i))
#define rep3(i, m, n) for (int i=(m); (i)<(int)(n); ++(i))
#define repr(i, n) for (int i=(int)(n)-1; (i)>=0; --(i))
#define rep3r(i, m, n) for (int i=(int)(n)-1; (i)>=(int)(m); --(i))
#define all(x) (x).begin(), (x).end()

const int INF = (int)(1e9);

struct edge {
	int to, cap, rev, cost;
	edge(int t, int ca, int r, int co) : to(t), cap(ca), rev(r), cost(co) {}
};
class MinCostFlow {
    int n;
    vector<int> dist, preve, prevv;
public:
    vector<vector<edge>> g;
    MinCostFlow(int n_) {
        n = n_;
        g.resize(n, vector<edge>());
        dist.resize(n), preve.resize(n), prevv.resize(n);
    }
    void add_edge(int f, int t, int ca, int co) {
        g[f].emplace_back(t, ca, (int)(g[t].size()), co);
        g[t].emplace_back(f, 0, (int)(g[f].size())-1, -co);
    }
    int min_cost_flow(int s, int t, int f) {
        int res = 0;
        while (f > 0) {
            dist.assign(n, INF);
            dist[s] = 0;
            bool update = true;
            while (update) {
                update = false;
                rep(v, n) if (dist[v] != INF) {
                    rep(e, g[v].size()) if (g[v][e].cap>0 && dist[g[v][e].to]>dist[v]+g[v][e].cost) {
                        dist[g[v][e].to] = dist[v] + g[v][e].cost;
                        preve[g[v][e].to] = e;
                        prevv[g[v][e].to] = v;
                        if (!update) update = true;
                    }
                }
            }
            if (dist[t] == INF) return -1;
            int d = f;
            for (int v=t; v!=s; v=prevv[v]) d = min(d, g[prevv[v]][preve[v]].cap);
            f -= d;
            res += d * dist[t];
            for (int v=t; v!=s; v=prevv[v]) {
                g[prevv[v]][preve[v]].cap -= d;
                g[v][g[prevv[v]][preve[v]].rev].cap += d;
            }
        }
        return res;
    }
};

int main() {
	int n;
	cin >> n;
	vector<vector<int>> w(n, vector<int>(n)), e(n, vector<int>(n));
	rep(i, n) rep(j, n) cin >> w[i][j];
	rep(i, n) rep(j, n) cin >> e[i][j];
	vector<string> f(n);
	rep(i, n) cin >> f[i];
	MinCostFlow mcf(2*n+2);
	rep(i, n) {
		int scost = 0;
		rep(j, n) if (f[i][j] == 'o') scost += e[i][j];
		rep(j, n) {
			int tcost = scost + (f[i][j]=='o' ? -e[i][j] : w[i][j]);
			mcf.add_edge(i, n+j, 1, tcost);
		}
	}
	rep(i, n) mcf.add_edge(2*n, i, 1, 0);
	rep(i, n) mcf.add_edge(n+i, 2*n+1, 1, 0);
	int res = mcf.min_cost_flow(2*n, 2*n+1, n);
	cout << res << endl;
	vector<tuple<int, int, string>> rlst;
	rep(i, n) for (const auto& ei : mcf.g[i]) if (ei.cap==0 && ei.cost>0) {
		rep(j, n) {
			if (j!=ei.to-n && f[i][j]=='o') rlst.emplace_back(i+1, j+1, "erase");
			if (j==ei.to-n && f[i][j]=='.') rlst.emplace_back(i+1, j+1, "write"); 
		}
	}
	int rlen = rlst.size();
	cout << rlen << endl;
	rep(i, rlen) cout << get<0>(rlst[i]) << ' ' << get<1>(rlst[i]) << ' ' << get<2>(rlst[i]) << endl;
	return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 4036
final_rank: 12

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 MinCostFlow{
    struct Edge{
        ll to;
        ll cap;
        ll cost;
        ll rev;
    };

    vector<vector<Edge>> G;
    ll N;
    vector<ll> h; // ポテンシャル
    vector<ll> dist; // 最短距離
    vector<ll> prevv; // 直前の頂点
    vector<ll> preve; // 直前の辺

    MinCostFlow(vector<vector<tuple<ll, ll, ll>>> G_){
        N = (ll)G_.size();
        G.resize(N);
        h.resize(N, 0);
        dist.resize(N, 0);
        prevv.resize(N, 0);
        preve.resize(N, 0);

        rep(from, N){
            for(tuple<ll, ll, ll> tmp : G_[from]){
                ll to = get<0>(tmp);
                ll cap = get<1>(tmp);
                ll cost = get<2>(tmp);
                G[from].pb((Edge){to, cap, cost, (ll)G[to].size()});
                G[to].pb((Edge){from, 0, -cost, (ll)G[from].size()-1});
            }
        }
    }

    //コストが非負実数の場合
    ll min_cost_flow1(ll s, ll t, ll f){
        vector<vector<Edge>> H(N);
        rep(from, N) for(Edge e : G[from]) H[from].pb(e);
        rep(i, N) h[i] = 0;

        ll res = 0;
        while(f > 0){
            //ダイクストラ法を用いてhを更新
            priority_queue<pll, vector<pll>, greater<pll>> PQ;
            rep(i, N) dist[i] = INF;
            dist[s] = 0;
            PQ.push({0, s});
            while(!PQ.empty()){
                pll p = PQ.top();
                PQ.pop();
                ll v = p.se;
                if(dist[v] < p.fi) continue;

                for(ll i=0; i<(ll)H[v].size(); i++){
                    Edge &e = H[v][i];
                    if(e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]){
                        dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
                        prevv[e.to] = v;
                        preve[e.to] = i;
                        PQ.push({dist[e.to], e.to});
                    }
                }
            }
            if(dist[t] == INF){
                //これ以上流せない
                return -1;
            }
            for(ll v=0; v<N; v++) h[v] += dist[v];

            // s-t間最短経路に沿って目一杯流す
            ll d = f;
            for(ll v=t; v!=s; v=prevv[v]){
                d = min(d, H[prevv[v]][preve[v]].cap);
            }
            f -= d;
            res += d*h[t];
            for(ll v=t; v!=s; v=prevv[v]){
                Edge &e = H[prevv[v]][preve[v]];
                e.cap -= d;
                H[v][e.rev].cap += d;
            }
        }

        return res;
    }

    //負のコストが存在する場合
    ll min_cost_flow2(ll s, ll t, ll f){
        ll res = 0;
        while(f > 0){
            //ベルマンフォード法により, s-t間最短経路を求める
            rep(i, N) dist[i] = INF;
            dist[s] = 0;
            bool update = true;
            while(update){
                update = false;
                rep(v, N){
                    if(dist[v]==INF) continue;
                    for(ll i=0; i<(ll)G[v].size(); i++){
                        Edge &e = G[v][i];
                        if(e.cap > 0 && dist[e.to] > dist[v] + e.cost){
                            dist[e.to] = dist[v] + e.cost;
                            prevv[e.to] = v;
                            preve[e.to] = i;
                            update = true;
                        }
                    }
                }
            }

            if(dist[t] == INF){
                //これ以上流せない
                return -1;
            }

            //s-t間最短経路に沿って目一杯流す
            ll d = f;
            for(ll v=t; v!=s; v=prevv[v]){
                d = min(d, G[prevv[v]][preve[v]].cap);
            }
            f -= d;
            res += d*dist[t];
            for(ll v=t; v!=s; v=prevv[v]){
                Edge &e = G[prevv[v]][preve[v]];
                e.cap -= d;
                G[v][e.rev].cap += d;
            }
        }

        return res;
    }

    vector<pll> match_pair(){
        vector<pll> res;
        ll n = (N-2)/2;
        rep(u, n){
            for(ll i=0; i<(ll)G[u].size(); i++){
                Edge e = G[u][i];
                if(n <= e.to && e.to < 2*n && e.cap==0){
                    res.pb({u, e.to-n});
                }
            }
        }

        return res;
    }
};

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    
    ll n;
    cin >> n;

    vector<vector<ll>> w(n, vector<ll>(n)), e(n, vector<ll>(n));
    rep(i, n) rep(j, n) cin >> w[i][j];
    rep(i, n) rep(j, n) cin >> e[i][j];

    vector<vector<char>> f(n, vector<char>(n));
    rep(i, n) rep(j, n) cin >> f[i][j];

    ll mcf = 0;
    rep(i, n) rep(j, n) if(f[i][j]=='o') mcf += e[i][j];

    vector<vector<tuple<ll, ll, ll>>> G(2*n+2);
    ll s = 2*n;
    ll t = 2*n+1;
    rep(i, n) G[s].pb({i, 1, 0});
    rep(i, n) rep(j, n){
        if(f[i][j]=='.'){
            G[i].pb({n+j, 1, w[i][j]});
        }else{
            G[i].pb({n+j, 1, -e[i][j]});
        }
    }
    rep(j, n) G[n+j].pb({t, 1, 0});

    MinCostFlow MCF(G);
    mcf += MCF.min_cost_flow2(s, t, n);
    cout << mcf << endl;
    
    vector<pll> mp = MCF.match_pair();
    vector<vector<char>> ans(n, vector<char>(n, '.'));
    for(pll tmp : mp){
        ll u = tmp.fi;
        ll v = tmp.se;
        ans[u][v] = 'o';
    }

    ll cnt = 0;
    rep(i, n) rep(j, n){
        if(f[i][j]!=ans[i][j]){
            cnt++;
        }
    }

    cout << cnt << endl;
    rep(i, n) rep(j, n){
        if(f[i][j] != ans[i][j]){
            if(f[i][j]=='.'){
                cout << i+1 << " " << j+1 << " write" << endl; 
            }else{
                cout << i+1 << " " << j+1 << " erase" << endl;
            }
        }
    }
}
accuracy: 1
time_ms: 10
memory_kb: 4616
final_rank: 16

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

#define rep(i,n) for(int i = 0; i < int(n); i++)
#define per(i,n) for(int i = (n) - 1; 0 <= i; i--)
#define rep2(i,l,r) for(int i = (l); i < int(r); i++)
#define per2(i,l,r) for(int i = (r) - 1; int(l) <= i; i--)
#define MM << " " <<
#define pb push_back
#define eb emplace_back
#define all(x) begin(x), end(x)
#define rall(x) rbegin(x), rend(x)
#define sz(x) (int)x.size()
#define each(e, x) for(atuo &e : x)

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

template<class T>
void print(const vector<T> &v, T x = 0) {
    int n = sz(v);
    rep(i,n) {
        cout << v[i] + x << (i == n - 1 ? '\n' : ' ');
    }
    if(v.empty()) cout << '\n';
}

using ll = long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;

template<class T>
bool chmax(T &x, const T &y) {
    return (x < y) ? (x = y, true) : false;
}

template<class T>
bool chmin(T &x, const T &y) {
    return (x > y) ? (x = y, true) : false;
}

struct MCF {
      struct edge {
          int to;
          ll cap, cost;
          int rev;
          edge(int to, ll cap, ll cost, int rev)
          : to(to), cap(cap), cost(cost), rev(rev) {}
      };

      vector<vector<edge>> es;
      vector<ll> d, h;
      vector<int> pre_v, pre_e;
      vector<pii> cash;
      ll INF = 1LL << 60;
      int n;

      MCF(int n) : es(n), d(n), h(n), pre_v(n), pre_e(n), cash(), n(n) {}

      int add_edge(int u, int v, int cap, ll cost) {
          es[u].eb(v, cap, cost, sz(es[v]));
          es[v].eb(u, 0 , -cost, sz(es[u]) - 1);
          int res = cash.size();
          cash.eb(u, sz(es[u]) - 1);
          return res;
      }

      void dijkstra(int s) {
          fill(all(d), INF);
          minheap<pll> q;
          q.emplace(d[s] = 0, s);
          while(!q.empty()) {
              auto [p, i] = q.top();
              q.pop();
              if(p > d[i]) continue;
              rep(j, sz(es[i])) {
                  edge e = es[i][j];
                  if(e.cap > 0 && chmin(d[e.to], d[i] + e.cost + h[i] - h[e.to])) {
                      assert(e.cost + h[i] - h[e.to] >= 0);
                      pre_v[e.to] = i;
                      pre_e[e.to] = j;
                      q.emplace(d[e.to], e.to);
                  }
              }
          }
      }

      ll mcf(int s, int t, ll flow) {
          ll ret = 0;
          while(flow > 0) {
              dijkstra(s);
              if(d[t] == INF) return -INF;
              rep(i,n) {
                  if(h[i] == INF || d[i] == INF) {
                      h[i] = INF;
                  } else {
                      h[i] += d[i];
                  }
              }
              ll f = flow;
              for(int i = t; i != s; i = pre_v[i]) {
                  chmin(f, es[pre_v[i]][pre_e[i]].cap);
              }
              assert(f > 0);
              ret += h[t] * f, flow -= f;
              for(int i = t; i != s; i = pre_v[i]) {
                  edge &e = es[pre_v[i]][pre_e[i]];
                  e.cap -= f;
                  es[i][e.rev].cap += f;
              }
          }
          return ret;
      }

      edge get_edge(int s) {
          assert(0 <= s);
          assert(s < sz(cash));
          auto [i, j] = cash[s];
          return es[i][j];
      }
    };

int main() {
    int n; cin >> n;
    vector<vector<ll>> w(n, vector<ll>(n));
    vector<vector<ll>> e(n, vector<ll>(n));
    vector<string> f(n);

    rep(i,n) rep(j,n) cin >> w[i][j];
    rep(i,n) rep(j,n) cin >> e[i][j];
    rep(i,n) cin >> f[i];

    MCF mcf(n * 2 + 2);
    const int S = n * 2, T = S + 1;
    vector<vector<ll>> id(n, vector<ll>(n));
    rep(i,n) {
        ll sm = 0;
        rep(j,n) {
            if(f[i][j] == 'o') sm += e[i][j];
        }
        rep(j,n) {
            if(f[i][j] == 'o') {
                id[i][j] = mcf.add_edge(i, j + n, 1, sm - e[i][j]);
            } else {
                id[i][j] = mcf.add_edge(i, j + n, 1, sm + w[i][j]);
            }
        }
    }

    rep(i,n) {
        mcf.add_edge(S, i, 1, 0);
        mcf.add_edge(i + n, T, 1, 0);
    }

    ll score = mcf.mcf(S, T, n);
    cout << score << endl;
    vector<pii> ans;
    rep(i,n) rep(j,n) {
        int k = f[i][j] == 'o' ? 1 : 0;
        if(mcf.get_edge(id[i][j]).cap == k) ans.eb(i, j);
    }

    cout << sz(ans) << endl;
    for(auto &i : ans) {
        if(f[i.first][i.second] == 'o') cout << i.first + 1 MM i.second + 1 MM "erase" << endl;
        else cout << i.first + 1 MM i.second + 1 MM "write" << endl;
    }
}
accuracy: 1
time_ms: 10
memory_kb: 4536
final_rank: 15

code_snippet: #include <bits/stdc++.h>
using std::cout;
using std::cin;
using std::endl;
namespace po167{
template<class Cap,class Cost>
struct min_cost_flow
{
	struct edge{
		int to;
		Cap cap;
		Cost cost;
	};
	std::vector<std::vector<edge>> G;
	std::vector<std::vector<int>> rev_edge;
	std::vector<std::pair<int,int>> edge_order;
	int _n;
	min_cost_flow(int n):G(n),rev_edge(n),_n(n){}
	int add_edge(int from,int to,Cap cap,Cost cost){
		assert(0<=from&&from<_n);
		assert(0<=to&&to<_n);
		edge_order.push_back({from,(int)(G[from].size())});
		rev_edge[to].push_back((int)(G[from].size()));
		G[from].push_back((edge){to,cap,cost});
		rev_edge[from].push_back((int)(G[to].size()));
		G[to].push_back((edge){from,(Cap)0,-cost});
		return (int)(edge_order.size());
	}
	std::vector<Cost> min_cost_slope(int s,int t){
		assert(0<=s&&s<_n);
		assert(0<=t&&t<_n);
		std::vector<Cost> ans={0};
		std::vector<Cost> pot(_n);
		while(true){
			std::priority_queue<std::pair<Cost,int>,std::vector<std::pair<Cost,int>>,std::greater<std::pair<Cost,int>>> pq;
			std::vector<int> front(_n);
			std::vector<Cost> seen(_n,std::numeric_limits<Cost>::max());
			seen[s]=0;
			pq.push({0,s});
			while(!pq.empty()){
				Cost time;
				int ind;
				Cost n_time;
				std::tie(time,ind)=pq.top();
				pq.pop();
				if(seen[ind]<time) continue;
				for(int i=0;i<(int)(G[ind].size());i++){
					auto x=G[ind][i];
					if(x.cap==0) continue;
					n_time=time+x.cost+pot[ind]-pot[x.to];
					if(seen[x.to]>n_time){
						seen[x.to]=n_time;
						front[x.to]=rev_edge[ind][i];
						pq.push({n_time,x.to});
					}
				}
			}
			/*for(int i=0;i<_n;i++){
				cout<<seen[i]<<" ";
			}
			cout<<endl;*/
			if(seen[t]==std::numeric_limits<Cost>::max()){
				break;
			}
			int ind=t;
			while(ind!=s){
				G[ind][front[ind]].cap++;
				int f=G[ind][front[ind]].to;
				G[f][rev_edge[ind][front[ind]]].cap--;
				ind=f;
			}
			ans.push_back(pot[t]-pot[s]+seen[t]);
			std::swap(pot,seen);
		}
		for(int i=1;i<(int)(ans.size());i++) ans[i+1]+=ans[i];
		return ans;
	}
	std::pair<Cap,Cost> flow(int s,int t){
		auto tmp=min_cost_slope(s,t);
		int x=tmp.size();
		return {(Cap)(x-1),tmp[x-1]};
	}
	std::tuple<int,int,Cap> report_edge(int i){
		int x,y;
		std::tie(x,y)=edge_order[i];
		int t=G[x][y].to;
		return {x,t,G[t][rev_edge[x][y]].cap};
	}
};
};



void solve();
// oddloop
int main() {
	solve();
}

void solve(){
	int n;
	cin>>n;
	std::vector<std::vector<int>> W(n,std::vector<int>(n)),E(n,std::vector<int>(n)),F(n,std::vector<int>(n));
	int tmp=0;
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++) cin>>W[i][j];
	}
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++) cin>>E[i][j];
	}
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			char c;
			cin>>c;
			if(c=='o'){
				F[i][j]=1;
				tmp+=E[i][j];
			}
		}
	}
	int M=2000;
	po167::min_cost_flow<int,int> G(n*2+2);
	int S=n*2,T=S+1;
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			if(F[i][j]) G.add_edge(i,j+n,1,M-E[i][j]);
			else G.add_edge(i,j+n,1,M+W[i][j]);
		}
	}
	for(int i=0;i<n;i++){
		G.add_edge(S,i,1,0);
		G.add_edge(i+n,T,1,0);
	}
	auto ans=G.flow(S,T);
	cout<<ans.second+tmp-M*n<<"\n";
	std::vector<std::pair<int,int>> p;
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			auto x=G.report_edge(i*n+j);
			int r=std::get<2>(x);
			if(r^F[i][j]) p.push_back({i,j});
		}
	}
	cout<<p.size()<<"\n";
	for(auto x:p){
		int s,t;
		std::tie(s,t)=x;
		cout<<s+1<<" "<<t+1<<" ";
		if(F[s][t]) cout<<"erase\n";
		else cout<<"write\n";
	}
}
accuracy: 1
time_ms: 10
memory_kb: 3940
final_rank: 10

code_snippet: #pragma region Macros
#include <bits/stdc++.h>
using namespace std;
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;
}
#ifdef DEBUG
template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << '(' << p.first << ',' << p.second << ')';
    return os;
}
template <class T> ostream &operator<<(ostream &os, const vector<T> &v) {
    os << '{';
    for(int i = 0; i < (int)v.size(); i++) {
        if(i) { os << ','; }
        os << v[i];
    }
    os << '}';
    return os;
}
void debugg() { cerr << endl; }
template <class T, class... Args>
void debugg(const T &x, const Args &... args) {
    cerr << " " << x;
    debugg(args...);
}
#define debug(...)                                                             \
    cerr << __LINE__ << " [" << #__VA_ARGS__ << "]: ", debugg(__VA_ARGS__)
#define dump(x) cerr << __LINE__ << " " << #x << " = " << (x) << endl
#else
#define debug(...) (void(0))
#define dump(x) (void(0))
#endif

struct Setup {
    Setup() {
        cin.tie(0);
        ios::sync_with_stdio(false);
        cout << fixed << setprecision(15);
    }
} __Setup;

using ll = long long;
#define OVERLOAD3(_1, _2, _3, name, ...) name
#define ALL(v) (v).begin(), (v).end()
#define RALL(v) (v).rbegin(), (v).rend()
#define REP1(i, n) for(int i = 0; i < (n); i++)
#define REP2(i, a, b) for(int i = (a); i < int(b); i++)
#define REP(...) OVERLOAD3(__VA_ARGS__, REP2, REP1)(__VA_ARGS__)
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
const int INF = 1 << 29;
const ll LLINF = 1LL << 60;
constexpr int MOD = 1000000007;
constexpr int MOD2 = 998244353;
const int dx[4] = {1, 0, -1, 0};
const int dy[4] = {0, 1, 0, -1};

void Case(int i) { cout << "Case #" << i << ": "; }
int popcount(int x) { return __builtin_popcount(x); }
ll popcount(ll x) { return __builtin_popcountll(x); }
#pragma endregion Macros

/*
○のついてる頂点を全て消したとみなす
oの頂点はコスト-a[i][j]
xの頂点はコストa[i][j]
とする
S -> (i行) -> (j列) -> T の順番で流す
S -> (i行), (j列) -> T はcap=1, cost=0
(i行) -> (j列) はcap=1, cost=a[i][j] or -a[i][j]
*/

template<typename Cap, typename Cost>
class MinCostFlow {
private:
    struct edge {
        int to, rev;
        Cap cap;
        Cost cost;
        edge() {}
        edge(int to, Cap cap, Cost cost, int rev):
            to(to), cap(cap), cost(cost), rev(rev) {}
    };

    int N;
    vector<vector<edge>> G;
    vector<Cost> h, dist;
    vector<int> prev_v, prev_e;
    Cost res;

    void bellman_ford(int S) {
        const Cost inf = numeric_limits<Cost>::max();
        fill(dist.begin(), dist.end(), inf);
        dist[S] = 0;
        bool upd = true;
        while(upd) {
            upd = false;
            for(int from = 0; from < N; from++) {
                if(dist[from] == inf) continue;
                for(int i = 0; i < (int)G[from].size(); i++) {
                    auto &e = G[from][i];
                    if(e.cap > 0 and dist[e.to] > dist[from] + e.cost) {
                        dist[e.to] = dist[from] + e.cost;
                        prev_v[e.to] = from;
                        prev_e[e.to] = i;
                        upd = true;
                    }
                }
            }
        }
    }

public:
    MinCostFlow() {}
    MinCostFlow(int N): N(N), G(N), h(N), dist(N), prev_v(N), prev_e(N) {}

    void add_edge(int from, int to, Cap cap, Cost cost) {
        G[from].emplace_back(to, cap, cost, (int)G[to].size());
        G[to].emplace_back(from, 0, -cost, (int)G[from].size() - 1);
    }

    bool build(int S, int T, Cap f = numeric_limits<Cap>::max()) {
        res = 0;
        const Cost inf = numeric_limits<Cost>::max();
        while(f > 0) {
            bellman_ford(S);
            if(dist[T] == inf) return false;

            Cap d = f;
            for(int v = T; v != S; v = prev_v[v]) {
                d = min(d, G[prev_v[v]][prev_e[v]].cap);
            }
            f -= d;
            res += d * dist[T];
            for(int v = T; v != S; v = prev_v[v]) {
                auto &e = G[prev_v[v]][prev_e[v]];
                e.cap -= d;
                G[v][e.rev].cap += d;
            }
        }
        return true;
    }

    Cost get_cost() { return res; }
    vector<vector<edge>> get_graph() { return G; }
};

int main() {
    int N;
    cin >> N;
    vector w(N, vector<int>(N));
    vector e(N, vector<int>(N));
    REP(i, N) REP(j, N) cin >> w[i][j];
    REP(i, N) REP(j, N) cin >> e[i][j];
    vector<string> g(N);
    REP(i, N) cin >> g[i];

    MinCostFlow<int, int> mcf(2*N+2);
    int S = 2*N, T = S+1;
    REP(i, N) {
        mcf.add_edge(S, i, 1, 0);
        mcf.add_edge(i+N, T, 1, 0);
    }
    int ans = 0;
    REP(i, N) REP(j, N) {
        if(g[i][j] == 'o') {
            ans += e[i][j];
            mcf.add_edge(i, j+N, 1, -e[i][j]);
        } else {
            mcf.add_edge(i, j+N, 1, w[i][j]);
        }
    }
    assert(mcf.build(S, T, N));
    ans += mcf.get_cost();
    auto G = mcf.get_graph();
    vector<tuple<int, int, string>> outputs;
    REP(i, N) {
        for(auto& e : G[i]) {
            if(N <= e.to and e.to < N*2) {
                int j = e.to - N;
                // debug(i, j, e.cap, e.cost, g[i][j]);
                if(g[i][j] == '.' and e.cap == 0) {
                    outputs.emplace_back(i+1, j+1, "write");
                }
                if(g[i][j] == 'o' and e.cap == 1) {
                    outputs.emplace_back(i+1, j+1, "erase");
                }
            }
        } 
    }
    cout << ans << endl;
    cout << outputs.size() << endl;
    for(auto [i, j, op] : outputs) {
        cout << i << ' ' << j << ' ' << op << endl;
    }
}
accuracy: 1
time_ms: 10
memory_kb: 4252
final_rank: 13

code_snippet: #include<bits/stdc++.h>
using namespace std;
//#include <atcoder/all>
//using namespace atcoder;
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 4000000000000000000 //オーバーフローに注意
#define MOD 1000000007
#define EPS 0.000000001




#define SIZE 405


//辺を表す構造体{行先、容量、コスト、逆辺のインデックス}
struct Edge{
	Edge(int arg_to,int arg_capacity,int arg_cost,int arg_rev_index){
		to = arg_to;
		capacity = arg_capacity;
		cost = arg_cost;
		rev_index = arg_rev_index;
	}

	int to,capacity,cost,rev_index;
};

struct Info{
	Info(int arg_row,int arg_col,bool arg_is_write){
		row = arg_row;
		col = arg_col;
		is_write = arg_is_write;
	}
	int row,col;
	bool is_write;
};

int V; //頂点数
vector<Edge> G[SIZE]; //グラフの隣接リスト表現
int dist[SIZE]; //最短距離
int pre_node[SIZE],pre_edge[SIZE]; //直前の頂点と辺

int W[105][105],E[105][105];
char table[105][105];


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

//sourceからsinkへの、流量flowの最小費用流を求める
//流せない場合は-1を返す
int min_cost_flow(int source,int sink,int flow){
	int ret = 0;
	while(flow > 0){
		//ベルマンフォード方により、source-sink間最短経路を求める
		for(int i = 0; i < V; i++)dist[i] = BIG_NUM;
		dist[source] = 0;
		bool update = true;
		while(update){
			update = false;
			for(int node_id = 0; node_id < V; node_id++){
				if(dist[node_id] == BIG_NUM)continue;
				for(int i = 0; i < G[node_id].size(); i++){
					Edge &e = G[node_id][i];
					if(e.capacity > 0 && dist[e.to] > dist[node_id]+e.cost){
						dist[e.to] = dist[node_id]+e.cost; //node_idを経由した方が早い場合
						pre_node[e.to] = node_id;
						pre_edge[e.to] = i;
						update = true;
					}
				}
			}
		}

		if(dist[sink] == BIG_NUM){
			//これ以上流せない
			return -1;
		}

		//source-sink間最短路に沿って目いっぱい流す
		int tmp_flow = flow;
		for(int node_id = sink; node_id != source; node_id = pre_node[node_id]){
			tmp_flow = min(tmp_flow,G[pre_node[node_id]][pre_edge[node_id]].capacity);
		}
		flow -= tmp_flow;
		ret += tmp_flow*dist[sink];
		for(int node_id = sink; node_id != source; node_id = pre_node[node_id]){
			Edge &e = G[pre_node[node_id]][pre_edge[node_id]];
			e.capacity -= tmp_flow;
			G[node_id][e.rev_index].capacity += tmp_flow;
		}
	}
	return ret;
}

int main(){

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

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

			scanf("%d",&W[i][k]);
		}
	}
	for(int i = 0; i < N; i++){
		for(int k = 0; k < N; k++){

			scanf("%d",&E[i][k]);
		}
	}

	int ans = 0;

	int source = 2*N;
	int sink = source+1;

	V = 2*N + 2;

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

		add_edge(source,row,1,0);
	}
	for(int col = 0; col < N; col++){

		add_edge(N+col,sink,1,0);
	}

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

		scanf("%s",table[row]);
		for(int col = 0; col < N; col++){
			if(table[row][col] == 'o'){

				//先に消しておく
				ans += E[row][col];
				add_edge(row,N+col,1,-E[row][col]); //消さないコスト

			}else{ //元々白

				add_edge(row,N+col,1,W[row][col]);
			}
		}
	}

	int C = min_cost_flow(source,sink,N);

	//printf("ans:%d C:%d\n",ans,C);
	printf("%d\n", ans+C);

	vector<Info> info;

	for(int row = 0; row < N; row++){
		for(int i = 0; i < G[row].size(); i++){

			int col = G[row][i].to-N;
			if(table[row][col] == 'o' && G[row][i].capacity > 0){ //消した

				info.push_back(Info(row+1,col+1,false));
			}
			if(table[row][col] == '.' && G[row][i].capacity == 0){ //書いた

				info.push_back(Info(row+1,col+1,true));
			}
		}
	}

	printf("%lld\n",info.size());
	for(int i = 0; i < info.size(); i++){

		printf("%d %d ",info[i].row,info[i].col);
		if(info[i].is_write){

			printf("write\n");
		}else{

			printf("erase\n");
		}
	}

	return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3576
final_rank: 2

code_snippet: #pragma GCC target("avx2")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
//#include <atcoder/all>
using namespace std;
//using namespace atcoder;
#define DEBUG
#ifdef DEBUG
template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << '(' << p.first << ',' << p.second << ')';
    return os;
}
template <class T> ostream &operator<<(ostream &os, const vector<T> &v) {
    os << '{';
    for(int i = 0; i < (int)v.size(); i++) {
        if(i) { os << ','; }
        os << v[i];
    }
    os << '}';
    return os;
}
void debugg() { cerr << endl; }
template <class T, class... Args>
void debugg(const T &x, const Args &... args) {
    cerr << " " << x;
    debugg(args...);
}
#define debug(...)                                                             \
    cerr << __LINE__ << " [" << #__VA_ARGS__ << "]: ", debugg(__VA_ARGS__)
#define dump(x) cerr << __LINE__ << " " << #x << " = " << (x) << endl
#else
#define debug(...) (void(0))
#define dump(x) (void(0))
#endif
using namespace std;
typedef long long ll;
typedef vector<ll> vl;
typedef vector<vl> vvl;
typedef vector<char> vc;
typedef vector<string> vs;
typedef vector<bool> vb;
typedef vector<double> vd;
typedef pair<ll,ll> P;
typedef pair<int,int> pii;
typedef vector<P> vpl;
typedef tuple<ll,ll,ll> tapu;
#define rep(i,n) for(int i=0; i<(n); i++)
#define REP(i,a,b) for(int i=(a); i<(b); i++)
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
const int inf = (1<<30)-1;
const ll linf = 1LL<<61;
const int MAX = 25000000;
int dy[8] = {0,1,0,-1,1,-1,-1,1};
int dx[8] = {-1,0,1,0,1,-1,1,-1};
const double pi = acos(-1);
const double eps = 1e-8;
template<typename T1,typename T2> inline bool chmin(T1 &a,T2 b){
	if(a>b){
		a = b; return true;
	}
	else return false;
}
template<typename T1,typename T2> inline bool chmax(T1 &a,T2 b){
	if(a<b){
		a = b; return true;
	}
	else return false;
}
template<typename T> inline void print(T &a){
    int sz = a.size();
    for(auto itr = a.begin(); itr != a.end(); itr++){
		cout << *itr;
        sz--;
        if(sz) cout << " ";
	}
    cout << "\n";
}
template<typename T1,typename T2> inline void print2(T1 a, T2 b){
	cout << a << " " << b << "\n";
}
template<typename T1,typename T2,typename T3> inline void print3(T1 a, T2 b, T3 c){
	cout << a << " " << b << " " << c << "\n";
}
void mark() {cout << "#" << "\n";}
ll pcount(ll x) {return __builtin_popcountll(x);}
//const int mod = 1e9 + 7;
const int mod = 998244353;

struct mcf{
    struct edge{
        ll to, cap, cost, rev;
    };
    int n;
    vector<vector<edge>> G;
    vector<ll> dist,prevv,preve;

    mcf(int n) : n(n) {
        G.resize(n);
        dist.resize(n);
        prevv.resize(n);
        preve.resize(n);
    }

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

    ll flow(ll s, ll t, ll f){
        ll res = 0;
        while(f > 0){
            fill(dist.begin(), dist.end(), linf);
            dist[s] = 0;
            bool upd = true;
            while(upd){
                upd = false;
                for(int u=0; u<n; u++){
                    if(dist[u] == linf) continue;
                    for(int i=0; i<G[u].size(); i++){
                        edge &e = G[u][i];
                        if(e.cap > 0 && dist[e.to] > dist[u] + e.cost){
                            dist[e.to] = dist[u] + e.cost;
                            prevv[e.to] = u;
                            preve[e.to] = i;
                            upd = true;
                        }
                    }
                }
            }

            if(dist[t] == linf) return -1;

            ll d = f;
            for(int u=t; u!=s; u=prevv[u]){
                d = min(d, G[prevv[u]][preve[u]].cap);
            }
            f -= d;
            res += d * dist[t];
            for(int u=t; u!=s; u=prevv[u]){
                edge &e = G[prevv[u]][preve[u]];
                e.cap -= d;
                G[u][e.rev].cap += d;
            }
        }
        return res;
    }
};

int main(){
    int n; cin >> n;
    vvl w(n,vl(n)), e(n,vl(n));
    rep(i,n) rep(j,n) cin >> w[i][j];
    rep(i,n) rep(j,n) cin >> e[i][j];
    vs s(n); rep(i,n) cin >> s[i];
    int mn = 0;
    rep(i,n) rep(j,n) if(s[i][j] == 'o') chmin(mn, -e[i][j]);
    mcf g(n*2+2);
    ll ans = 0;
    int S = n*2, T = n*2+1;
    rep(i,n){
        g.add_edge(S,i,1,0);
        g.add_edge(i+n,T,1,0);
    }
    rep(i,n) rep(j,n){
        if(s[i][j] == '.'){
            g.add_edge(i,j+n,1,w[i][j]-mn);
        }else{
            ans += e[i][j];
            g.add_edge(i,j+n,1,-e[i][j]-mn);
        }
    }
    ans += g.flow(S,T,n) + mn*n;
    cout << ans << "\n";
    vl y,x;
    vs op;
    rep(i,n){
        for(auto e : g.G[i]){
            if(n <= e.to && e.to < n*2){
                int j = e.to - n;
                if(s[i][j] == '.' && e.cap == 0){
                    y.push_back(i+1); x.push_back(j+1);
                    op.push_back("write");
                }
                if(s[i][j] == 'o' && e.cap == 1){
                    y.push_back(i+1); x.push_back(j+1);
                    op.push_back("erase");
                }
            }
        }
    }
    cout << y.size() << "\n";
    rep(i,y.size()){
        cout << y[i] << " " << x[i] << " " << op[i] << "\n";
    }
}
accuracy: 1
time_ms: 10
memory_kb: 4520
final_rank: 14

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 4000000000000000000 //オーバーフローに注意
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


#define SIZE 205


//辺を表す構造体{行先、容量、コスト、逆辺のインデックス}
struct Edge{
	Edge(int arg_to,int arg_capacity,int arg_cost,int arg_rev_index){
		to = arg_to;
		capacity = arg_capacity;
		cost = arg_cost;
		rev_index = arg_rev_index;
	}

	int to,capacity,cost,rev_index;
};

struct Info{
	Info(int arg_row,int arg_col,bool arg_is_erase){
		row = arg_row;
		col = arg_col;
		is_erase = arg_is_erase;
	}

	int row,col;
	bool is_erase;
};

int V; //頂点数
vector<Edge> G[SIZE]; //グラフの隣接リスト表現
int dist[SIZE]; //最短距離
int pre_node[SIZE],pre_edge[SIZE]; //直前の頂点と辺
int W[SIZE][SIZE],E[SIZE][SIZE];
char table[SIZE][SIZE];


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

//sourceからsinkへの、流量flowの最小費用流を求める
//流せない場合は-1を返す
int min_cost_flow(int source,int sink,int flow){
	int ret = 0;
	while(flow > 0){
		//ベルマンフォード方により、source-sink間最短経路を求める
		for(int i = 0; i < V; i++)dist[i] = BIG_NUM;
		dist[source] = 0;
		bool update = true;
		while(update){
			update = false;
			for(int node_id = 0; node_id < V; node_id++){
				if(dist[node_id] == BIG_NUM)continue;
				for(int i = 0; i < G[node_id].size(); i++){
					Edge &e = G[node_id][i];
					if(e.capacity > 0 && dist[e.to] > dist[node_id]+e.cost){
						dist[e.to] = dist[node_id]+e.cost; //node_idを経由した方が早い場合
						pre_node[e.to] = node_id;
						pre_edge[e.to] = i;
						update = true;
					}
				}
			}
		}

		if(dist[sink] == BIG_NUM){
			//これ以上流せない
			return -1;
		}

		//source-sink間最短路に沿って目いっぱい流す
		int tmp_flow = flow;
		for(int node_id = sink; node_id != source; node_id = pre_node[node_id]){
			tmp_flow = min(tmp_flow,G[pre_node[node_id]][pre_edge[node_id]].capacity);
		}
		flow -= tmp_flow;
		ret += tmp_flow*dist[sink];
		for(int node_id = sink; node_id != source; node_id = pre_node[node_id]){
			Edge &e = G[pre_node[node_id]][pre_edge[node_id]];
			e.capacity -= tmp_flow;
			G[node_id][e.rev_index].capacity += tmp_flow;
		}
	}
	return ret;
}

int main(){

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

	int source = 2*N;
	int sink = source+1;
	V = sink+1;

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

			scanf("%d",&W[i][k]);
		}
	}
	for(int i = 0; i < N; i++){
		for(int k = 0; k < N; k++){

			scanf("%d",&E[i][k]);
		}
	}

	int sum = 0;

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

		scanf("%s",table[row]);
		for(int col = 0; col < N; col++){

			if(table[row][col] == 'o'){

				sum += E[row][col]; //消したことにする
			}
		}
	}

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

		add_edge(source,row,1,0);
	}
	for(int col = 0;col < N; col++){

		add_edge(col+N,sink,1,0);
	}

	for(int row = 0; row < N; row++){
		for(int col = 0; col < N; col++){
			if(table[row][col] == 'o'){

				add_edge(row,col+N,1,-E[row][col]); //消さなかったことにする
			}else{

				add_edge(row,col+N,1,W[row][col]); //新たに書き込むコスト
			}
		}
	}

	sum += min_cost_flow(source,sink,N);

	printf("%d\n",sum);

	vector<Info> info;

	for(int row = 0; row < N; row++){
		for(int i = 0; i < G[row].size(); i++){
			int col = G[row][i].to-N;
			if(table[row][col] == 'o' && G[row][i].capacity == 1){ //消えたまま

				info.push_back(Info(row,col,true));
			}
			if(table[row][col] == '.' && G[row][i].capacity == 0){ //書き込み

				info.push_back(Info(row,col,false));
			}
		}
	}

	printf("%lld\n",info.size());

	for(int i = 0; i < info.size(); i++){

		printf("%d %d",info[i].row+1,info[i].col+1);
		if(info[i].is_erase){

			printf(" erase\n");
		}else{

			printf(" write\n");
		}
	}


	return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3660
final_rank: 7

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 4000000000000000000 //オーバーフローに注意
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;



#define NUM 205

typedef pair<int,int> P; //firstは最短距離、secondは頂点の番号

struct Info{
	Info(int arg_row,int arg_col,bool arg_is_del){
		row = arg_row;
		col = arg_col;
		is_del = arg_is_del;
	}
	int row,col;
	bool is_del;
};

//辺を表す構造体{行先、容量、コスト、逆辺のインデックス}
struct Edge{
	Edge(int arg_to,int arg_capacity,int arg_cost,int arg_rev_index){
		to = arg_to;
		capacity = arg_capacity;
		cost = arg_cost;
		rev_index = arg_rev_index;
	}

	int to,capacity,cost,rev_index;
};

int V; //頂点数
vector<Edge> G[NUM]; //グラフの隣接リスト表現
int dist[NUM]; //最短距離
int pre_node[NUM],pre_edge[NUM]; //直前の頂点と辺
int W[NUM][NUM],E[NUM][NUM];
char table[NUM][NUM];

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

//sourceからsinkへの、流量flowの最小費用流を求める
//流せない場合は-1を返す
int min_cost_flow(int source,int sink,int flow){
	int ret = 0;
	while(flow > 0){
		//ベルマンフォード方により、source-sink間最短経路を求める
		for(int i = 0; i < V; i++)dist[i] = BIG_NUM;
		dist[source] = 0;
		bool update = true;
		while(update){
			update = false;
			for(int node_id = 0; node_id < V; node_id++){
				if(dist[node_id] == BIG_NUM)continue;
				for(int i = 0; i < G[node_id].size(); i++){
					Edge &e = G[node_id][i];
					if(e.capacity > 0 && dist[e.to] > dist[node_id]+e.cost){
						dist[e.to] = dist[node_id]+e.cost; //node_idを経由した方が早い場合
						pre_node[e.to] = node_id;
						pre_edge[e.to] = i;
						update = true;
					}
				}
			}
		}

		if(dist[sink] == BIG_NUM){
			//これ以上流せない
			return -1;
		}

		//source-sink間最短路に沿って目いっぱい流す
		int tmp_flow = flow;
		for(int node_id = sink; node_id != source; node_id = pre_node[node_id]){
			tmp_flow = min(tmp_flow,G[pre_node[node_id]][pre_edge[node_id]].capacity);
		}
		flow -= tmp_flow;
		ret += tmp_flow*dist[sink];
		for(int node_id = sink; node_id != source; node_id = pre_node[node_id]){
			Edge &e = G[pre_node[node_id]][pre_edge[node_id]];
			e.capacity -= tmp_flow;
			G[node_id][e.rev_index].capacity += tmp_flow;
		}
	}
	return ret;
}

int main(){

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

	int source = 2*N;
	int sink = source+1;
	V = sink+1;

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

			scanf("%d",&W[i][k]);
		}
	}
	for(int i = 0; i < N; i++){
		for(int k = 0; k < N; k++){

			scanf("%d",&E[i][k]);
		}
	}

	int sum = 0;

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

		scanf("%s",table[row]);
		for(int col = 0; col < N; col++){
			if(table[row][col] == 'o'){

				sum += E[row][col]; //先に全ての〇を消す
			}
		}
	}

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

		add_edge(source,row,1,0);
	}
	for(int col = 0; col < N; col++){

		add_edge(col+N,sink,1,0);
	}

	for(int row = 0; row < N; row++){
		for(int col = 0; col < N; col++){
			if(table[row][col] == 'o'){

				add_edge(row,col+N,1,-E[row][col]); //元々計上していたコストを消す

			}else{

				add_edge(row,col+N,1,W[row][col]); //新たに書き込むコスト
			}
		}
	}

	sum += min_cost_flow(source,sink,N);

	vector<Info> info;

	for(int row = 0; row < N; row++){
		for(int i = 0; i < G[row].size(); i++){
			int col = G[row][i].to-N;

			if(table[row][col] == 'o' && G[row][i].capacity == 1){ //逆辺に流れた

				//FLOW += E[row][col];
				info.push_back(Info(row,col,true));
			}
			if(table[row][col] != 'o' && G[row][i].capacity == 0){ //辺に流れた

				info.push_back(Info(row,col,false));
			}
		}
	}

	printf("%d\n",sum);

	printf("%lld\n",info.size());

	for(int i = 0; i < info.size(); i++){

		printf("%d %d",info[i].row+1,info[i].col+1);
		if(info[i].is_del){

			printf(" erase\n");
		}else{

			printf(" write\n");
		}
	}

	return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3636
final_rank: 5

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


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


#include <algorithm>
#include <utility>
#include <vector>

namespace atcoder {
namespace internal {

template <class E> struct csr {
    std::vector<int> start;
    std::vector<E> elist;
    explicit csr(int n, const std::vector<std::pair<int, E>>& edges)
        : start(n + 1), elist(edges.size()) {
        for (auto e : edges) {
            start[e.first + 1]++;
        }
        for (int i = 1; i <= n; i++) {
            start[i] += start[i - 1];
        }
        auto counter = start;
        for (auto e : edges) {
            elist[counter[e.first]++] = e.second;
        }
    }
};

}  // namespace internal

}  // namespace atcoder


#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, class Cost> struct mcf_graph {
  public:
    mcf_graph() {}
    explicit mcf_graph(int n) : _n(n) {}

    int add_edge(int from, int to, Cap cap, Cost cost) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        assert(0 <= cost);
        int m = int(_edges.size());
        _edges.push_back({from, to, cap, 0, cost});
        return m;
    }

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

    edge get_edge(int i) {
        int m = int(_edges.size());
        assert(0 <= i && i < m);
        return _edges[i];
    }
    std::vector<edge> edges() { return _edges; }

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

        int m = int(_edges.size());
        std::vector<int> edge_idx(m);

        auto g = [&]() {
            std::vector<int> degree(_n), redge_idx(m);
            std::vector<std::pair<int, _edge>> elist;
            elist.reserve(2 * m);
            for (int i = 0; i < m; i++) {
                auto e = _edges[i];
                edge_idx[i] = degree[e.from]++;
                redge_idx[i] = degree[e.to]++;
                elist.push_back({e.from, {e.to, -1, e.cap - e.flow, e.cost}});
                elist.push_back({e.to, {e.from, -1, e.flow, -e.cost}});
            }
            auto _g = internal::csr<_edge>(_n, elist);
            for (int i = 0; i < m; i++) {
                auto e = _edges[i];
                edge_idx[i] += _g.start[e.from];
                redge_idx[i] += _g.start[e.to];
                _g.elist[edge_idx[i]].rev = redge_idx[i];
                _g.elist[redge_idx[i]].rev = edge_idx[i];
            }
            return _g;
        }();

        auto result = slope(g, s, t, flow_limit);

        for (int i = 0; i < m; i++) {
            auto e = g.elist[edge_idx[i]];
            _edges[i].flow = _edges[i].cap - e.cap;
        }

        return result;
    }

  private:
    int _n;
    std::vector<edge> _edges;

    struct _edge {
        int to, rev;
        Cap cap;
        Cost cost;
    };

    std::vector<std::pair<Cap, Cost>> slope(internal::csr<_edge>& g,
                                            int s,
                                            int t,
                                            Cap flow_limit) {

        std::vector<std::pair<Cost, Cost>> dual_dist(_n);
        std::vector<int> prev_e(_n);
        std::vector<bool> vis(_n);
        struct Q {
            Cost key;
            int to;
            bool operator<(Q r) const { return key > r.key; }
        };
        std::vector<int> que_min;
        std::vector<Q> que;
        auto dual_ref = [&]() {
            for (int i = 0; i < _n; i++) {
                dual_dist[i].second = std::numeric_limits<Cost>::max();
            }
            std::fill(vis.begin(), vis.end(), false);
            que_min.clear();
            que.clear();

            size_t heap_r = 0;

            dual_dist[s].second = 0;
            que_min.push_back(s);
            while (!que_min.empty() || !que.empty()) {
                int v;
                if (!que_min.empty()) {
                    v = que_min.back();
                    que_min.pop_back();
                } else {
                    while (heap_r < que.size()) {
                        heap_r++;
                        std::push_heap(que.begin(), que.begin() + heap_r);
                    }
                    v = que.front().to;
                    std::pop_heap(que.begin(), que.end());
                    que.pop_back();
                    heap_r--;
                }
                if (vis[v]) continue;
                vis[v] = true;
                if (v == t) break;
                Cost dual_v = dual_dist[v].first, dist_v = dual_dist[v].second;
                for (int i = g.start[v]; i < g.start[v + 1]; i++) {
                    auto e = g.elist[i];
                    if (!e.cap) continue;
                    Cost cost = e.cost - dual_dist[e.to].first + dual_v;
                    if (dual_dist[e.to].second - dist_v > cost) {
                        Cost dist_to = dist_v + cost;
                        dual_dist[e.to].second = dist_to;
                        prev_e[e.to] = e.rev;
                        if (dist_to == dist_v) {
                            que_min.push_back(e.to);
                        } else {
                            que.push_back(Q{dist_to, e.to});
                        }
                    }
                }
            }
            if (!vis[t]) {
                return false;
            }

            for (int v = 0; v < _n; v++) {
                if (!vis[v]) continue;
                dual_dist[v].first -= dual_dist[t].second - dual_dist[v].second;
            }
            return true;
        };
        Cap flow = 0;
        Cost cost = 0, prev_cost_per_flow = -1;
        std::vector<std::pair<Cap, Cost>> result = {{Cap(0), Cost(0)}};
        while (flow < flow_limit) {
            if (!dual_ref()) break;
            Cap c = flow_limit - flow;
            for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                c = std::min(c, g.elist[g.elist[prev_e[v]].rev].cap);
            }
            for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                auto& e = g.elist[prev_e[v]];
                e.cap += c;
                g.elist[e.rev].cap -= c;
            }
            Cost d = -dual_dist[s].first;
            flow += c;
            cost += c * d;
            if (prev_cost_per_flow == d) {
                result.pop_back();
            }
            result.push_back({flow, cost});
            prev_cost_per_flow = d;
        }
        return result;
    }
};

}  // namespace atcoder

using namespace atcoder;

int main() {
    int N;
    cin >> N;
    vector W(N, vector(N, 0));
    vector E(N, vector(N, 0));
    vector<string> F(N);
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            cin >> W[i][j];
        }
    }
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            cin >> E[i][j];
        }
    }
    for (int i = 0; i < N; i++) {
        cin >> F[i];
    }
    mcf_graph<int, int> G(2 + N + N * N + N);
    int S = 0, T = 1;
    int bef = 0, now = 2;
    for (int i = 0; i < N; i++) {
        G.add_edge(bef, now + i, 1, 0);
    }
    bef = now;
    now += N;
    int maine = now;
    for (int i = 0; i < N; i++) {
        int tot = 0;
        for (int j = 0; j < N; j++) {
            if (F[i][j] == 'o')
                tot += E[i][j];
        }
        for (int j = 0; j < N; j++) {
            G.add_edge(bef + i, now + i * N + j, 1, (F[i][j] == 'o' ? tot - E[i][j] : tot + W[i][j]));
        }
    }
    bef = now;
    now += N * N;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            G.add_edge(bef + i * N + j, now + j, 1, 0);
        }
    }
    bef = now;
    now += N * N;
    for (int i = 0; i < N; i++) {
        G.add_edge(bef + i, T, 1, 0);
    }
    cout << G.flow(S, T).second << endl;
    vector<pair<int, int>> er, wr;
    for (auto&& e : G.edges()) {
        if (e.to >= maine && e.to < maine + N * N) {
            int idx = e.to - maine;
            int x = idx / N, y = idx % N;
            if (e.flow == 1 && F[x][y] == '.') {
                wr.emplace_back(x, y);
            } else if (e.flow == 0 && F[x][y] == 'o') {
                er.emplace_back(x, y);
            }
        }
    }
    cout << wr.size() + er.size() << endl;
    for (auto&& [x, y] : wr) {
        cout << x + 1 << " " << y + 1 << " "
             << "write" << endl;
    }
    for (auto&& [x, y] : er) {
        cout << x + 1 << " " << y + 1 << " "
             << "erase" << endl;
    }
}
accuracy: 1
time_ms: 10
memory_kb: 5292
final_rank: 17

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


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


#include <algorithm>
#include <utility>
#include <vector>

namespace atcoder {
namespace internal {

template <class E> struct csr {
    std::vector<int> start;
    std::vector<E> elist;
    explicit csr(int n, const std::vector<std::pair<int, E>>& edges)
        : start(n + 1), elist(edges.size()) {
        for (auto e : edges) {
            start[e.first + 1]++;
        }
        for (int i = 1; i <= n; i++) {
            start[i] += start[i - 1];
        }
        auto counter = start;
        for (auto e : edges) {
            elist[counter[e.first]++] = e.second;
        }
    }
};

}  // namespace internal

}  // namespace atcoder


#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, class Cost> struct mcf_graph {
  public:
    mcf_graph() {}
    explicit mcf_graph(int n) : _n(n) {}

    int add_edge(int from, int to, Cap cap, Cost cost) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        assert(0 <= cost);
        int m = int(_edges.size());
        _edges.push_back({from, to, cap, 0, cost});
        return m;
    }

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

    edge get_edge(int i) {
        int m = int(_edges.size());
        assert(0 <= i && i < m);
        return _edges[i];
    }
    std::vector<edge> edges() { return _edges; }

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

        int m = int(_edges.size());
        std::vector<int> edge_idx(m);

        auto g = [&]() {
            std::vector<int> degree(_n), redge_idx(m);
            std::vector<std::pair<int, _edge>> elist;
            elist.reserve(2 * m);
            for (int i = 0; i < m; i++) {
                auto e = _edges[i];
                edge_idx[i] = degree[e.from]++;
                redge_idx[i] = degree[e.to]++;
                elist.push_back({e.from, {e.to, -1, e.cap - e.flow, e.cost}});
                elist.push_back({e.to, {e.from, -1, e.flow, -e.cost}});
            }
            auto _g = internal::csr<_edge>(_n, elist);
            for (int i = 0; i < m; i++) {
                auto e = _edges[i];
                edge_idx[i] += _g.start[e.from];
                redge_idx[i] += _g.start[e.to];
                _g.elist[edge_idx[i]].rev = redge_idx[i];
                _g.elist[redge_idx[i]].rev = edge_idx[i];
            }
            return _g;
        }();

        auto result = slope(g, s, t, flow_limit);

        for (int i = 0; i < m; i++) {
            auto e = g.elist[edge_idx[i]];
            _edges[i].flow = _edges[i].cap - e.cap;
        }

        return result;
    }

  private:
    int _n;
    std::vector<edge> _edges;

    struct _edge {
        int to, rev;
        Cap cap;
        Cost cost;
    };

    std::vector<std::pair<Cap, Cost>> slope(internal::csr<_edge>& g,
                                            int s,
                                            int t,
                                            Cap flow_limit) {

        std::vector<std::pair<Cost, Cost>> dual_dist(_n);
        std::vector<int> prev_e(_n);
        std::vector<bool> vis(_n);
        struct Q {
            Cost key;
            int to;
            bool operator<(Q r) const { return key > r.key; }
        };
        std::vector<int> que_min;
        std::vector<Q> que;
        auto dual_ref = [&]() {
            for (int i = 0; i < _n; i++) {
                dual_dist[i].second = std::numeric_limits<Cost>::max();
            }
            std::fill(vis.begin(), vis.end(), false);
            que_min.clear();
            que.clear();

            size_t heap_r = 0;

            dual_dist[s].second = 0;
            que_min.push_back(s);
            while (!que_min.empty() || !que.empty()) {
                int v;
                if (!que_min.empty()) {
                    v = que_min.back();
                    que_min.pop_back();
                } else {
                    while (heap_r < que.size()) {
                        heap_r++;
                        std::push_heap(que.begin(), que.begin() + heap_r);
                    }
                    v = que.front().to;
                    std::pop_heap(que.begin(), que.end());
                    que.pop_back();
                    heap_r--;
                }
                if (vis[v]) continue;
                vis[v] = true;
                if (v == t) break;
                Cost dual_v = dual_dist[v].first, dist_v = dual_dist[v].second;
                for (int i = g.start[v]; i < g.start[v + 1]; i++) {
                    auto e = g.elist[i];
                    if (!e.cap) continue;
                    Cost cost = e.cost - dual_dist[e.to].first + dual_v;
                    if (dual_dist[e.to].second - dist_v > cost) {
                        Cost dist_to = dist_v + cost;
                        dual_dist[e.to].second = dist_to;
                        prev_e[e.to] = e.rev;
                        if (dist_to == dist_v) {
                            que_min.push_back(e.to);
                        } else {
                            que.push_back(Q{dist_to, e.to});
                        }
                    }
                }
            }
            if (!vis[t]) {
                return false;
            }

            for (int v = 0; v < _n; v++) {
                if (!vis[v]) continue;
                dual_dist[v].first -= dual_dist[t].second - dual_dist[v].second;
            }
            return true;
        };
        Cap flow = 0;
        Cost cost = 0, prev_cost_per_flow = -1;
        std::vector<std::pair<Cap, Cost>> result = {{Cap(0), Cost(0)}};
        while (flow < flow_limit) {
            if (!dual_ref()) break;
            Cap c = flow_limit - flow;
            for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                c = std::min(c, g.elist[g.elist[prev_e[v]].rev].cap);
            }
            for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                auto& e = g.elist[prev_e[v]];
                e.cap += c;
                g.elist[e.rev].cap -= c;
            }
            Cost d = -dual_dist[s].first;
            flow += c;
            cost += c * d;
            if (prev_cost_per_flow == d) {
                result.pop_back();
            }
            result.push_back({flow, cost});
            prev_cost_per_flow = d;
        }
        return result;
    }
};

}  // namespace atcoder

using namespace atcoder;

int main() {
    int N;
    cin >> N;
    vector W(N, vector(N, 0));
    vector E(N, vector(N, 0));
    vector<string> F(N);
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            cin >> W[i][j];
        }
    }
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            cin >> E[i][j];
        }
    }
    for (int i = 0; i < N; i++) {
        cin >> F[i];
    }
    mcf_graph<int, int> G(1000000);
    int S = 0, T = 1;
    int bef = 0, now = 2;
    for (int i = 0; i < N; i++) {
        G.add_edge(bef, now + i, 1, 0);
    }
    bef = now;
    now += N;
    int maine = now;
    for (int i = 0; i < N; i++) {
        int tot = 0;
        for (int j = 0; j < N; j++) {
            if (F[i][j] == 'o')
                tot += E[i][j];
        }
        for (int j = 0; j < N; j++) {
            G.add_edge(bef + i, now + i * N + j, 1, (F[i][j] == 'o' ? tot - E[i][j] : tot + W[i][j]));
        }
    }
    bef = now;
    now += N * N;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            G.add_edge(bef + i * N + j, now + j, 1, 0);
        }
    }
    bef = now;
    now += N * N;
    for (int i = 0; i < N; i++) {
        G.add_edge(bef + i, T, 1, 0);
    }
    cout << G.flow(S, T).second << endl;
    vector<pair<int, int>> er, wr;
    for (auto&& e : G.edges()) {
        if (e.to >= maine && e.to < maine + N * N) {
            int idx = e.to - maine;
            int x = idx / N, y = idx % N;
            if (e.flow == 1 && F[x][y] == '.') {
                wr.emplace_back(x, y);
            } else if (e.flow == 0 && F[x][y] == 'o') {
                er.emplace_back(x, y);
            }
        }
    }
    cout << wr.size() + er.size() << endl;
    for (auto&& [x, y] : wr) {
        cout << x + 1 << " " << y + 1 << " "
             << "write" << endl;
    }
    for (auto&& [x, y] : er) {
        cout << x + 1 << " " << y + 1 << " "
             << "erase" << endl;
    }
}
accuracy: 1
time_ms: 100
memory_kb: 20192
final_rank: 19

code_snippet: #include <bits/stdc++.h>
using namespace std;
template <class T>
class Edmonds_Karp {
    T inf = numeric_limits<T> :: max() / 2;
    class edge {
        public:
        int nxt, to;
        T c, f;
    };
    vector<edge> e;
    vector<T> d;
    vector<int> head;
    vector<int> prev;
    vector<int> pree;
    vector<int> vis;
    int cnt;
    int source;
    int sink;

    public:
    Edmonds_Karp() {}
    Edmonds_Karp(int N, int M, int _source, int _sink) : source(_source), sink(_sink), head(N + 1), vis(N + 1), d(N + 1), prev(N + 1), pree(N + 1), e(M * 2 + 3) {
        cnt = 1;
    }

    void link(int u, int v, T f, T c) {
        e[++cnt].nxt = head[u];
        head[u] = cnt;
        e[cnt].f = f;
        e[cnt].to = v;
        e[cnt].c = c;
    }
    void add_edge(int u, int v, T f, T c) {
        link(u, v, f, c);
        link(v, u, 0, -c);
    }  
    bool spfa() {
        d.assign(d.size(), -1);
        d[source] = 0;
        queue<int> q;
        q.push(source);
        while(!q.empty()) {
            int u = q.front();
            q.pop();
            vis[u] = 0;
            for(int i = head[u]; i; i = e[i].nxt) { 
                if(e[i].f && (d[e[i].to] > d[u] + e[i].c || d[e[i].to] == -1)) {
                    pree[e[i].to] = i;
                    prev[e[i].to] = u;
                    d[e[i].to] = d[u] + e[i].c;
                    if(vis[e[i].to] == 0) {
                        q.push(e[i].to);
                        vis[e[i].to] = 1;
                    }
                }
            }
        }
        return ~d[sink]; 
    }
    T _Edmonds_Karp() {
        T cost = 0;
        T flow = 0;
        while(spfa()) {
            int now = sink;
            T delta = inf;
            while(now != source) {
                delta = min(delta, (T)e[pree[now]].f);
                now = prev[now];
            }
            now = sink;
            while(now != source) {
                e[pree[now]].f -= delta;
                e[pree[now] ^ 1].f += delta; 
                now = prev[now];
            }
            flow += delta;
            cost += (T)delta * d[sink];
        } 
        return cost;
    }
    void print(vector<string> S) {
      vector<tuple<int, int, string>> Ans;
      int N = S.size();
      for (int i = 0; i < N; ++i) {
        for (int j = head[i]; j; j = e[j].nxt) {
          if (e[j].f == 0) {
            int k = e[j].to - N;
            if (0 <= k && k < N) {
              if (S[i][k] == 'o') {
                for (int P = 0; P < N; ++P) {
                  if (P != k && S[i][P] == 'o') {
                    Ans.emplace_back(i, P, "erase");
                  }
                }
                break;
              } else {
                for (int P = 0; P < N; ++P) {
                  if (S[i][P] == 'o') {
                    Ans.emplace_back(i, P, "erase");
                  }
                }
                Ans.emplace_back(i, k, "write");
              }
            }
          }
        }
      }
      cout << Ans.size() << '\n';
      for (auto [X, Y, O] : Ans) {
        cout << X + 1 << ' ' << Y + 1 << ' ' << O << '\n';
      }
    }
};
int main() {
  int N;
  cin >> N;
  vector<vector<int>> W(N, vector<int> (N));
  for (int i = 0; i < N; ++i) {
    for (int j = 0; j < N; ++j) {
      cin >> W[i][j];
    }
  }
  vector<vector<int>> E(N, vector<int> (N));
  for (int i = 0; i < N; ++i) {
    for (int j = 0; j < N; ++j) {
      cin >> E[i][j];
    }
  }
  vector<string> S(N);
  for (int i = 0; i < N; ++i) {
    cin >> S[i];
  }
  int source = N + N;
  int sink = N + N + 1;
  Edmonds_Karp<int> flow(N + N + 2, N * N * 5, source, sink);
  for (int i = 0; i < N; ++i) {
    int V = 0;
    for (int j = 0; j < N; ++j) {
      if (S[i][j] == 'o') {
        V += E[i][j]; 
      }
    }
    for (int j = 0; j < N; ++j) {
      if (S[i][j] == 'o') {
        flow.add_edge(i, j + N, 1, V - E[i][j]);
      } else {
        flow.add_edge(i, j + N, 1, V + W[i][j]); 
      } 
    }
  }
  for (int i = 0; i < N; ++i) {
    flow.add_edge(source, i, 1, 0);
  }
  for (int i = 0; i < N; ++i) {
    flow.add_edge(i + N, sink, 1, 0);
  }
  cout << flow._Edmonds_Karp() << '\n';
  flow.print(S);
}
accuracy: 1
time_ms: 20
memory_kb: 5052
final_rank: 18

code_snippet: #include <iostream>
#include <math.h>
#include <iomanip>
#include <limits>
// #include <atcoder/all>
// using namespace atcoder;
#include <cstring>
#include <string>
#include <vector>
#include <algorithm>
#define rep(i,n) for (int i = 0;i < (n); ++i)
using namespace std;
using ll = long long;
using P = pair<int,int>;
#define chmax(x,y) x = max(x,y)
using R=double;
using ld = long double;
int INF = 1e9;
ll LINF = 1e18;
int mod = 1000000007;
//using mint = modint1000000007;
int mod2 = 1;
using graph = vector<vector<int>>;
#define MV 1000
struct edge{int to,cap,cost,rev;};
int V;
vector<edge> G[MV];
int dist[MV];
int prevv[MV],preve[MV];
vector<P> circle;

void add_edge(int from,int to,int cap,int cost){
	//if(from == 0) printf("to is %d\n",to);
	G[from].push_back((edge){to,cap,cost,(int)(G[to].size())});
	G[to].push_back((edge){from,0,-cost,(int)(G[from].size()-1)});
}

int min_cost_flow(int s,int t,int f){
	int res = 0;
	while(f > 0){
		fill(dist,dist+V,INF);
		dist[s] = 0;
		bool update = true;
		while(update){
			update = false;
			rep(i,V){
				//cout << i << " " << dist[i] << endl;
				if(dist[i] == INF) continue;
				for(int j=0;j<G[i].size();j++){
					edge &e = G[i][j];
					if(e.cap>0&&dist[e.to]>dist[i]+e.cost){
						//cout << i << " " << e.to << " " << e.cost<<endl;
						dist[e.to] = dist[i]+e.cost;
						prevv[e.to] = i;
						preve[e.to] = j;
						update = true;
					}
				}
			}
		}

		

		if(dist[t]==INF) return -1;

		int d = f;
		for(int v=t;v!=s;v=prevv[v]) d = min(d,G[prevv[v]][preve[v]].cap);
		//for(int v=t;v!=s;v=prevv[v]) printf("%d %d %d\n",v,prevv[v],G[prevv[v]][preve[v]].cap);
		f -= d;
		// cout <<" " << f << endl;;
		// int du;cin >> du;
		res += d*dist[t];
		for(int v=t;v!=s;v=prevv[v]) {
			edge &e = G[prevv[v]][preve[v]];
			e.cap -= d;
			G[v][e.rev].cap += d;
		}
	}
	return res;
}

int wtable[105][105];
int etable[105][105];
int anstable[105][105];
int main(){
	// rep(i,105)rep(j,105) wtable[i][j] = 0;
	// rep(i,105)rep(j,105) etable[i][j] = 0;
	// rep(i,105)rep(j,105) anstable[i][j] = 0;
	rep(i,MV) prevv[i] = 0;
	rep(i,MV) preve[i] = 0;
	int n;cin >> n;
	V = 2*n+2;
	int st = 2*n;
	int go = st+1;
	rep(i,n)rep(j,n) cin >> wtable[i][j];
	rep(i,n)rep(j,n) cin >> etable[i][j];
	rep(i,n) {
		add_edge(st,i,1,0);
		add_edge(i+n,go,1,0);
	}
	int num = 0;
	int ind = 0;
	rep(i,n)rep(j,n){
		char temp;cin >> temp;
		if(temp == '.') add_edge(i,j+n,1,wtable[i][j]);
		else {
			add_edge(i,j+n,1,-etable[i][j]);
			num += etable[i][j];
			anstable[i][j] = -1;
			ind++;
		}
	}
	num += min_cost_flow(st,go,n);
	rep(i,n){
		rep(j,G[i].size()){
			if(G[i][j].cap == 0) circle.emplace_back(i,G[i][j].to);
		}
	}
	for(P p:circle) {
		//printf("%d %d\n",p.first,p.second);
		anstable[p.first][p.second-n]++;
	}
	int temp = 0;
	rep(i,n)rep(j,n) if(anstable[i][j]!= 0) temp++;
	cout << num << endl;
	cout << temp << endl;

	rep(i,n)rep(j,n) {
		if(anstable[i][j]==1) printf("%d %d write\n",i+1,j+1);
		else if(anstable[i][j] == -1) printf("%d %d erase\n",i+1,j+1);
	}
	//cout << ind << endl;
	

	


}
accuracy: 1
time_ms: 10
memory_kb: 3652
final_rank: 6

code_snippet: #include<iostream>
using namespace std;
//Minimum Cost Flow O(FE log V)
#include<algorithm>
#include<utility>
#include<vector>
#include<queue>
#include<limits>
template<typename T>
struct MCF{
	struct edge{
		int to,rev,cap;
		T cost;
	};
	vector<vector<edge> >G;
	vector<T>h,d;
	vector<int>pv,pe;
	MCF(int n_=0):G(n_),h(n_,0),d(n_),pv(n_),pe(n_){}
	int add_edge(int from,int to,int cap,T cost)
	{
		G[from].push_back({to,(int)G[to].size(),cap,cost});
		G[to].push_back({from,(int)G[from].size()-1,0,-cost});
		return (int)G[from].size()-1;
	}
	T min_cost_flow(int s,int t,int f)//ans or -1
	{
		T ret=0;
		while(f>0)
		{
			priority_queue<pair<T,int>,vector<pair<T,int> >,greater<pair<T,int> > >P;
			fill(d.begin(),d.end(),numeric_limits<T>::max());
			d[s]=0;
			P.push(make_pair(0,s));
			while(!P.empty())
			{
				pair<T,int>p=P.top();P.pop();
				if(d[p.second]<p.first)continue;
				for(int i=0;i<G[p.second].size();i++)
				{
					edge&e=G[p.second][i];
					if(e.cap>0&&d[e.to]>d[p.second]+e.cost+h[p.second]-h[e.to])
					{
						d[e.to]=d[p.second]+e.cost+h[p.second]-h[e.to];
						pv[e.to]=p.second;
						pe[e.to]=i;
						P.push(make_pair(d[e.to],e.to));
					}
				}
			}
			if(d[t]==numeric_limits<T>::max())return -1;
			for(int u=0;u<G.size();u++)h[u]+=d[u];
			int d=f;
			for(int u=t;u!=s;u=pv[u])d=min(d,G[pv[u]][pe[u]].cap);
			f-=d;
			ret+=d*h[t];
			for(int u=t;u!=s;u=pv[u])
			{
				G[pv[u]][pe[u]].cap-=d;
				G[u][G[pv[u]][pe[u]].rev].cap+=d;
			}
		}
		return ret;
	}
};
int N;
string S[100];
int W[100][100],F[100][100];
int ei[100][100];
int O[100];
main()
{
	cin>>N;
	for(int i=0;i<N;i++)for(int j=0;j<N;j++)cin>>W[i][j];
	for(int i=0;i<N;i++)for(int j=0;j<N;j++)cin>>F[i][j];
	MCF<int>P(2*N+2);
	int st=2*N,go=2*N+1;
	for(int i=0;i<N;i++)
	{
		P.add_edge(N+i,go,1,0);
		P.add_edge(st,i,1,0);
		cin>>S[i];
		int cost=0;
		for(int j=0;j<N;j++)if(S[i][j]=='o')cost+=F[i][j];
		for(int j=0;j<N;j++)
		{
			int now=cost;
			if(S[i][j]=='o')now-=F[i][j];
			else now+=W[i][j];
			ei[i][j]=P.add_edge(i,N+j,1,now);
		}
	}
	cout<<P.min_cost_flow(st,go,N)<<endl;
	for(int i=0;i<N;i++)for(int j=0;j<N;j++)
	{
		if(P.G[i][ei[i][j]].cap==0)O[i]=j;
	}
	vector<pair<int,int> >C;
	for(int i=0;i<N;i++)for(int j=0;j<N;j++)
	{
		if(O[i]==j)
		{
			if(S[i][j]=='.')C.push_back(make_pair(i,j));
		}
		else
		{
			if(S[i][j]=='o')C.push_back(make_pair(i,j));
		}
	}
	cout<<C.size()<<endl;
	for(pair<int,int>p:C)
	{
		cout<<p.first+1<<" "<<p.second+1<<" ";
		if(S[p.first][p.second]=='o')cout<<"erase"<<endl;
		else cout<<"write"<<endl;
	}
}
accuracy: 1
time_ms: 10
memory_kb: 3564
final_rank: 1

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

typedef long long ll;
#define rep(i, n) for(int i = 0; i < n; ++ i)

using cap_type = int;
using cost_type = int;

struct edge {
    int to, rev;
    cap_type cap;
    cost_type cost;
    edge(int t, cap_type c, cost_type ct, int r) : to(t), rev(r), cap(c), cost(ct) {}
};
using graph = vector<vector<edge>>;

void add_edge(graph& g, int from, int to, cap_type cap, cost_type cost) {
    g[from].emplace_back(to, cap, cost, g[to].size());
    g[to].emplace_back(from, 0, -cost, g[from].size() - 1);
}

cost_type min_cost_flow(graph& g, int s, int t, cap_type f) {
    static_assert(!is_floating_point<cap_type>::value, "");
    using P = pair<cost_type, int>;
    const auto inf = numeric_limits<cost_type>::max() / 2;
    cost_type res = 0;
    vector<cost_type> h(g.size()), dist(g.size());
    vector<int> prevv(g.size()), preve(g.size());
    while(f > 0) {
        priority_queue<P, vector<P>, greater<>> que;
        fill(std::begin(dist), std::end(dist), inf);
        dist[s] = 0;
        que.emplace(dist[s], s);
        while(!que.empty()) {
            const auto cur_d = que.top().first;
            const int v = que.top().second;
            que.pop();
            if(dist[v] < cur_d) continue;
            for(int i = 0; i < (int)g[v].size(); ++i) {
                auto& e = g[v][i];
                if(e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]) {
                    dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
                    prevv[e.to] = v;
                    preve[e.to] = i;
                    que.emplace(dist[e.to], e.to);
                }
            }
        }
        if(dist[t] == inf) return -1;
        for(int v = 0; v < (int)g.size(); ++v) {
            h[v] += dist[v];
        }

        auto d = f;
        for(int v = t; v != s; v = prevv[v]) {
            d = min(d, g[prevv[v]][preve[v]].cap);
        }
        f -= d;
        res += d * h[t];
        for(int v = t; v != s; v = prevv[v]) {
            auto& e = g[prevv[v]][preve[v]];
            e.cap -= d;
            g[v][e.rev].cap += d;
        }
    }
    return res;
}

int n;

int main() {
    cin >> n;
    vector<vector<int>> w(n, vector<int>(n));
    vector<vector<int>> e(n, vector<int>(n));
    vector<vector<char>> f(n, vector<char>(n));
    rep(i, n) {
        rep(j, n) {
            cin >> w[i][j];
        }
    }
    rep(i, n) {
        rep(j, n) {
            cin >> e[i][j];
        }
    }
    rep(i, n) {
        rep(j, n) {
            cin >> f[i][j];
        }
    }
    graph g(2 * n + 2, vector<edge>());
    int s = 2 * n, t = 2 * n + 1;
    rep(i, n) {
        add_edge(g, s, i, 1, 0);
        add_edge(g, i + n, t, 1, 0);
    }
    rep(i, n) {
        rep(j, n) {
            int cost = 0;
            if(f[i][j] == '.') cost += w[i][j] * 2;
            rep(k, n) {
                if(k != j and f[i][k] == 'o') cost += e[i][k];
            }
            rep(k, n) {
                if(k != i and f[k][j] == 'o') cost += e[k][j];
            }
            // cerr << i << " " << j << " " << cost << endl;
            add_edge(g, i, j + n, 1, cost);
        }
    }
    int mincost = min_cost_flow(g, s, t, n);
    cout << mincost / 2 << endl;
    vector<pair<pair<int, int>, string>> vec;
    vector<vector<char>> fi(n, vector<char>(n, '.'));
    rep(i, n) {
        for(auto e: g[i]) {
            int j = e.to, cp = e.cap;
            // cerr << i << " " << j << " " << cp << endl;
            if(cp == 0) {
                fi[i][j - n] = 'o';
            }
        }
    }
    rep(i, n) {
        rep(j, n) {
            if(fi[i][j] == 'o' and f[i][j] == '.') {
                vec.push_back({{i, j}, "write"});
            }
            if(fi[i][j] == '.' and f[i][j] == 'o') {
                vec.push_back({{i, j}, "erase"});
            }
        }
    }
    int sz = (int)vec.size();
    cout << sz << endl;
    rep(i, sz) {
        cout << vec[i].first.first + 1 << " " << vec[i].first.second + 1 << " " << vec[i].second << endl;
    }
}
accuracy: 1
time_ms: 10
memory_kb: 3660
final_rank: 7

code_snippet: #include <bits/stdc++.h>
using namespace std;
const int INF = 1000000000;
template <typename Cap, typename Cost>
struct primal_dual{
  struct edge{
    int to, rev;
    Cap cap;
    Cost cost;
    edge(int to, int rev, Cap cap, Cost cost): to(to), rev(rev), cap(cap), cost(cost){
    }
  };
  int N;
  vector<vector<edge>> G;
  primal_dual(int N): N(N), G(N){
  }
  void add_edge(int from, int to, Cap cap, Cost cost){
    int id1 = G[from].size();
    int id2 = G[to].size();
    G[from].push_back(edge(to, id2, cap, cost));
    G[to].push_back(edge(from, id1, 0, - cost));
  }
  Cost min_cost_flow(int s, int t, Cap F){
    Cap flow = 0;
    Cost cost = 0;
    vector<Cost> h(N, 0);
    while (flow < F){
      vector<Cap> m(N, INF);
      vector<Cost> d(N, INF);
      vector<int> pv(N, -1);
      vector<int> pe(N, -1);
      vector<bool> used(N, false);
      priority_queue<pair<Cost, int>, vector<pair<Cost, int>>, greater<pair<Cost, int>>> pq;
      pq.push(make_pair(0, s));
      d[s] = 0;
      while (!pq.empty()){
        int v = pq.top().second;
        pq.pop();
        if (!used[v]){
          used[v] = true;
          if (v == t){
            break;
          }
          int cnt = G[v].size();
          for (int i = 0; i < cnt; i++){
            int w = G[v][i].to;
            if (!used[w] && G[v][i].cap > 0){
              Cost tmp = G[v][i].cost - h[w] + h[v];
              if (d[w] > d[v] + tmp){
                d[w] = d[v] + tmp;
                m[w] = min(m[v], G[v][i].cap);
                pv[w] = v;
                pe[w] = i;
                pq.push(make_pair(d[w], w));
              }
            }
          }
        }
      }
      if (!used[t]){
        break;
      }
      for (int i = 0; i < N; i++){
        if (used[i]){
          h[i] -= d[t] - d[i];
        }
      }
      Cap c = min(m[t], F - flow);
      for (int i = t; i != s; i = pv[i]){
        G[pv[i]][pe[i]].cap -= c;
        G[i][G[pv[i]][pe[i]].rev].cap += c;
      }
      flow += c;
      cost += c * (- h[s]);
    }
    return cost;
  }
};
int main(){
  int n;
  cin >> n;
  vector<vector<int>> W(n, vector<int>(n));
  for (int i = 0; i < n; i++){
    for (int j = 0; j < n; j++){
      cin >> W[i][j];
    }
  }
  vector<vector<int>> E(n, vector<int>(n));
  for (int i = 0; i < n; i++){
    for (int j = 0; j < n; j++){
      cin >> E[i][j];
    }
  }
  vector<string> F(n);
  for (int i = 0; i < n; i++){
    cin >> F[i];
  }
  primal_dual<int, int> G(n * 2 + 2);
  for (int i = 0; i < n; i++){
    G.add_edge(0, i + 1, 1, 0);
  }
  for (int i = 0; i < n; i++){
    for (int j = 0; j < n; j++){
      if (F[i][j] == '.'){
        G.add_edge(1 + i, 1 + n + j, 1, 1000 + W[i][j]);
      }
      if (F[i][j] == 'o'){
        G.add_edge(1 + i, 1 + n + j, 1, 1000 - E[i][j]);
      }
    }
  }
  for (int i = 0; i < n; i++){
    G.add_edge(1 + n + i, 1 + n + n, 1, 0);
  }
  int sum = 0;
  for (int i = 0; i < n; i++){
    for (int j = 0; j < n; j++){
      if (F[i][j] == 'o'){
        sum += E[i][j];
      }
    }
  }
  cout << sum + G.min_cost_flow(0, n * 2 + 1, n) - 1000 * n << endl;
  vector<vector<char>> S(n, vector<char>(n, '.'));
  for (int i = 1; i <= n; i++){
    for (auto e : G.G[i]){
      if (e.to > 0 && e.cap == 0){
        S[i - 1][e.to - n - 1] = 'o';
      }
    }
  }
  vector<tuple<int, int, string>> op;
  for (int i = 0; i < n; i++){
    for (int j = 0; j < n; j++){
      if (S[i][j] == 'o' && F[i][j] == '.'){
        op.push_back(make_tuple(i, j, "write"));
      }
      if (S[i][j] == '.' && F[i][j] == 'o'){
        op.push_back(make_tuple(i, j, "erase"));
      }
    }
  }
  int m = op.size();
  cout << m << endl;
  for (int i = 0; i < m; i++){
    cout << get<0>(op[i]) + 1 << ' ' << get<1>(op[i]) + 1 << ' ' << get<2>(op[i]) << endl;
  }
}
accuracy: 1
time_ms: 10
memory_kb: 3580
final_rank: 3

code_snippet: #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
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; }
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define mp make_pair
#define si(x) int(x.size())
const int mod=1000000007,MAX=205;
const ll INF=1<<30;
typedef pair<int,int> P;

struct edge{int to,cap,cost,rev;};

int V;
vector<edge> G[MAX];
int h[MAX];
int dist[MAX];
int prevv[MAX],preve[MAX];

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

int min_cost_flow(int s,int t,int f){
    int res=0;
    fill(h,h+V,0);
    
    while(f>0){
        priority_queue<P,vector<P>,greater<P>> que;
        fill(dist,dist+V,INF);
        dist[s]=0;
        que.push(P(0,s));
        while(!que.empty()){
            P p=que.top();que.pop();
            int v=p.second;
            if(dist[v]<p.first) continue;
            for(int i=0;i<G[v].size();i++){
                edge &e=G[v][i];
                if(e.cap>0&&dist[e.to]>dist[v]+e.cost+h[v]-h[e.to]){
                    dist[e.to]=dist[v]+e.cost+h[v]-h[e.to];
                    prevv[e.to]=v;
                    preve[e.to]=i;
                    que.push(P(dist[e.to],e.to));
                }
            }
        }
        
        if(dist[t]==INF){
            return -1;
        }
        for(int v=0;v<V;v++){
            h[v]+=dist[v];
        }
        
        int d=f;
        for(int v=t;v!=s;v=prevv[v]){
            d=min(d,G[prevv[v]][preve[v]].cap);
        }
        f-=d;
        res+=d*h[t];
        for(int v=t;v!=s;v=prevv[v]){
            edge &e=G[prevv[v]][preve[v]];
            e.cap-=d;
            G[v][e.rev].cap+=d;
        }
    }
    return res;
}//sからtへの流量fの最小費用流、流せない場合は-1

int main(){
    
    std::ifstream in("text.txt");
    std::cin.rdbuf(in.rdbuf());
    cin.tie(0);
    ios::sync_with_stdio(false);
    
    int N;cin>>N;
    vector<vector<int>> A(N,vector<int>(N)),B(N,vector<int>(N));
    for(int i=0;i<N;i++) for(int j=0;j<N;j++) cin>>A[i][j];
    for(int i=0;i<N;i++) for(int j=0;j<N;j++) cin>>B[i][j];
    vector<string> S(N);
    for(int i=0;i<N;i++) cin>>S[i];
    
    V=2*N+2;
    
    for(int i=0;i<N;i++){
        for(int j=0;j<N;j++){
            int cost=0;
            
            for(int k=0;k<N;k++){
                if(j==k){
                    if(S[i][k]=='.') cost+=A[i][k];
                }else{
                    if(S[i][k]=='o') cost+=B[i][k];
                }
            }
            
            add_edge(i,N+j,1,cost);
        }
        add_edge(2*N,i,1,0);
        add_edge(N+i,2*N+1,1,0);
    }
    
    int x=min_cost_flow(2*N,2*N+1,N);
    
    vector<pair<int,int>> C,D;
    
    for(int i=0;i<N;i++){
        for(auto j:G[i]){
            if(j.to>=N&&j.cap==0){
                for(int k=0;k<N;k++){
                    if(j.to-N==k){
                        if(S[i][k]=='.') C.push_back(mp(i+1,k+1));
                    }else{
                        if(S[i][k]=='o') D.push_back(mp(i+1,k+1));
                    }
                }
            }
        }
    }
    
    cout<<x<<endl;
    
    cout<<si(C)+si(D)<<endl;
    
    for(auto a:C) cout<<a.fi<<" "<<a.se<<" "<<"write"<<endl;
    for(auto a:D) cout<<a.fi<<" "<<a.se<<" "<<"erase"<<endl;
    
}
accuracy: 1
time_ms: 10
memory_kb: 3632
final_rank: 4