Problem:
まるかいて

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

ある時、太郎君は次のゲームを思いつきました。

n×nの格子状のマス目を書いておきます。

それぞれのマス目の初期状態は、丸印が書かれているか、書かれていないかのどちらか一方です。

これらの丸印を消したり書いたりして最終的にどの一列を見ても必ずちょうど1つのみの丸印が、どの一行を見ても必ず1つのみの丸印が存在するようにすることが目標であり、この状態にすればゲームをクリアしたことになります。

太郎君はこのゲームを思いつきましたが、太郎君はこのゲームをクリアするのに大変時間がかかってしまいます。そこで、大学生であるあなたに助けを求めました。

太郎君の兄であり大学生であるあなたの仕事は以下の通りです。

厳密な状況を考えるために、あるマス目に丸印を書き込むコスト、あるマス目にある丸印を消すコストをあなたは導き出しました。このコストを用いてこのゲームをクリアするためにかかる操作のコストを最小化するような手順を考える。
このとき、最小のコストおよびそのコストを達成するような手順を出力するプログラムを書いてください。
出力については、最小コストを達成する手順なら、どのような操作、順番でも出力してもよいものとする。

Input

n
W11 W12 .. W1n
W21 W22 .. W2n
..
Wn1 Wn2 .. Wnn
E11 E12 .. E1n
E21 E22 .. E2n
..
En1 En2 .. Enn
F1(n文字)
F2(n文字)
..
Fn(n文字)

nは太郎君の作ったマス目が一辺にいくつあるかを表す

Wijは上からi番目、左からj番目のマス目に丸印を書き込むコストを表す

Eijは上からi番目、左からj番目のマス目に書かれてある丸印を消すコストを表す

Fiは上からi番目の行のマス目の初期状態を表す

Fiの左からj文字目について

'o'のとき、上からi番目、左からj番目のマス目に丸印が書かれてあることを表す。

'.'のとき、上からi番目、左からj番目のマス目が空白であることを表す。

Constraints

1≤ n ≤ 100
1≤ Wij ≤ 1000
1≤ Eij ≤ 1000

Fiは文字列であり、その長さはnである

Fiは'o'と'.'のみで構成されている

Output

mincost
cnt
R1 C1 operate1
R2 C2 operate2
..
Rcnt Ccnt operatecnt

mincostは、太郎君のゲームをクリアするために必要な最小コストを表す。

mincostは書き込み操作、消去操作で発生するコストの総和で計算される。

cnt : mincostのコストを達成する操作を行った回数を表す

k回目(1≤k≤cnt)に実行する操作はk+2行目に記述する

k回目(1≤k≤cnt)の操作に対して

上からi番目のマス目、左からj番目のマス目に対して行ったものとすると

Rkkである。

この操作が丸印を消す操作であるならばoperatek = "erase"とせよ

この操作が丸印を書き込む操作であるならばoperatek = "write"とせよ

Rk,Ck,operatekは一行に空白区切りで出力しなければならない

丸印の書かれてあるマス目に対して丸印を記述する操作、および丸印が書かれていないマス目に対して丸印を消去する操作をした場合は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となる。

Solution in C++14 (judge verdict: Accepted)

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <deque>
#include <list>
#include <queue>
#include <stack>
#include <vector>
#include <utility>
#include <algorithm>
#include <map>
#include <set>
#include <complex>
#include <cmath>
#include <limits>
#include <cfloat>
#include <climits>
#include <ctime>
#include <cassert>
#include <numeric>
#include <functional>
#include <bitset>

using namespace std;
#define int long long int
const int INF = 1001001001001001LL;
const int MOD = 1000000007;

int di[] = {-1, 0, 1, 0, -1, 1, 1, -1};
int dj[] = {0, 1, 0, -1, 1, 1, -1, -1};



// 最小費用流
// O(F E log V)
// 与えられるもの
// 有向グラフ G = (V, E)
// 各辺 e に対して, 容量 u(e) >= 0
// 各辺 e に対して, 費用 c(e) (負でも ok)

// 使い方
// PrimalDual(V) (コンストラクタ)
// add_edge(int from, int to, flow_t cap, cost_t cost)
// veryfied : http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=GRL_6_B&lang=jp


template< typename flow_t, typename cost_t >
struct PrimalDual{
    const cost_t INF;

    struct edge {
        int to;
        flow_t cap;
        cost_t cost;
        int rev;
        bool isrev;
    };
    vector<vector<edge>> graph;
    vector<cost_t> potential, min_cost;
    vector<int> prevv, preve;

    PrimalDual(int V) : graph(V), INF(numeric_limits< cost_t >::max()){}

    void add_edge(int from, int to, flow_t cap, cost_t cost){
        graph[from].emplace_back((edge) {to, cap, cost, (int)graph[to].size(), false});
        graph[to].emplace_back((edge) {from, 0, -cost, (int)graph[from].size() - 1, true});
    }

    cost_t min_cost_flow(int s, int t, flow_t f){
        int V = (int) graph.size();
        cost_t ret = 0;
        using Pi = pair< cost_t, int >;
        priority_queue< Pi, vector< Pi >, greater< Pi > > que;
        potential.assign(V, 0);
        preve.assign(V, -1);
        prevv.assign(V, -1);

        while(f > 0){
            min_cost.assign(V, INF);
            que.emplace(0, s);
            min_cost[s] = 0;
            while(!que.empty()){
                Pi p = que.top();
                que.pop();
                if(min_cost[p.second] < p.first) continue;
                for(int i = 0; i < graph[p.second].size(); i++){
                    edge &e = graph[p.second][i];
                    cost_t nextCost = min_cost[p.second] + e.cost + potential[p.second] - potential[e.to];
                    if(e.cap > 0 && min_cost[e.to] > nextCost){
                        min_cost[e.to] = nextCost;
                        prevv[e.to] = p.second, preve[e.to] = i;
                        que.emplace(min_cost[e.to], e.to);
                    }
                }
            }
            if(min_cost[t] == INF) return -1;
            for(int v = 0; v < V; v++) potential[v] += min_cost[v];
            flow_t addflow = f;
            for(int v = t; v != s; v = prevv[v]){
                addflow = min(addflow, graph[prevv[v]][preve[v]].cap);
            }
            f -= addflow;
            ret += addflow * potential[t];
            for(int v = t; v != s; v = prevv[v]){
                edge &e = graph[prevv[v]][preve[v]];
                e.cap -= addflow;
                graph[v][e.rev].cap += addflow;
            }
        }
        return ret;
    }

    void output(vector<pair<pair<int, int>, int>> &ans){
        for(int i = 0; i < graph.size(); i++){
            for(auto &e : graph[i]) {
                if(e.isrev) continue;
                auto &rev_e = graph[e.to][e.rev];
                cout << i << "->" << e.to << " (flow: " << rev_e.cap << "/" << rev_e.cap + e.cap << ")" << endl;
            }
        }
    }
};

signed main(){
    
    int n; 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));
    
    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++) cin >> F[i][j];
    
    int tmpCost = 0;
    for(int i = 0; i < n; i++){
       for(int j = 0; j < n; j++){
            if(F[i][j] == 'o') tmpCost += E[i][j];
        }
    }

    int V = 2 * n + 2;
    PrimalDual<int, int> pd(V);

    // 0 := s, 2 * n + 1 := t とする

    // s から (1, 0) をはる
    for(int i = 1; i <= n; i++){
        pd.add_edge(0, i, 1, 0);
    }

    // t へ (1, 0) をはる
    for(int i = n + 1; i <= 2 * n; i++){
        pd.add_edge(i, 2 * n + 1, 1, 0);
    }

    for(int i = 0; i < n; i++){
        for(int j = 0; j < n; j++){
            int from = i + 1;
            int to = j + 1 + n;
            int cap = 1;
            int cost = (F[i][j] == '.') ? W[i][j] : -E[i][j];
            pd.add_edge(from, to, cap, cost);
        }
    }

    cout << pd.min_cost_flow(0, 2 * n + 1, n) + tmpCost << endl;
    vector<pair<pair<int, int>, string>> ans;
    for(int i = 0; i < pd.graph.size(); i++){
        for(auto &e : pd.graph[i]) {
            if(e.isrev) continue;
            auto &rev_e = pd.graph[e.to][e.rev];
            if(i == 0) continue;
            if(e.to == 2 * n + 1) continue;
            int from = i - 1;
            int to = e.to - 1 - n;
            if(rev_e.cap == 1 and F[from][to] == '.') ans.push_back({{from + 1, to + 1}, "write"});
            if(rev_e.cap == 0 and F[from][to] == 'o') ans.push_back({{from + 1, to + 1}, "erase"});
            
        }
    }
    cout << ans.size() << endl;
    for(int i = 0; i < ans.size(); i++) cout << ans[i].first.first << " " << ans[i].first.second << " " << ans[i].second << endl;

    return 0;
}