import * as fs from 'fs' // import * as readline from 'readline' // const rl = readline.createInterface({ input: process.stdin, output: process.stdout }) // const ask = (query: string) => new Promise((resolve) => rl.question(query, resolve)) // // Don't forget `rl.close()`. const INT = Math.floor declare global { interface Array { last(): T | undefined isEmpty(): boolean } } Array.prototype.last = function () { return this.length === 0 ? undefined : this[this.length - 1] } Array.prototype.isEmpty = function () { return this.length === 0 } const less = (a: T, b: T) => (a == b ? 0 : a < b ? -1 : 1) const greater = (a: T, b: T) => (a == b ? 0 : a < b ? 1 : -1) const bigIntMax = (...args: bigint[]) => args.reduce((m, e) => (e > m ? e : m)) const bigIntMin = (...args: bigint[]) => args.reduce((m, e) => (e < m ? e : m)) const bigIntAbs = (arg: bigint) => (arg < 0 ? -arg : arg) declare const stdin: number function read_stdin() { return fs.readFileSync(process.env.NODE_ENV === 'debug' ? stdin : process.stdin.fd, 'utf8') } class Input { readonly inputs: string[] private index = 0 constructor(str?: string) { this.inputs = (str ? str : read_stdin()).split(/\s+/) } number() { return Number(this.inputs[this.index++]) } numbers(n: number) { return this.inputs.slice(this.index, (this.index += n)).map(Number) } bigint() { return BigInt(this.inputs[this.index++]) } bigints(n: number) { return this.inputs.slice(this.index, (this.index += n)).map(BigInt) } word() { return this.inputs[this.index++] } words(n: number) { return this.inputs.slice(this.index, (this.index += n)) } } function array(len: number, init: T): T[] { return Array(len).fill(init) } function array2(h: number, w: number, init: T): T[][] { return array(h, 0).map(() => array(w, init)) } type Edge = { to: number; cap: number; rev: Edge } class Dinic { private level: number[] private edges: Edge[][] private edges2: Edge[][] constructor(private V: number) { this.level = Array(V) this.edges = [...Array(V)].map(() => []) this.edges2 = [...Array(V)].map(() => []) } getEdges(from: number) { return this.edges2[from] } addEdge(from: number, to: number, cap: number) { const e: Edge = { to, cap } as any const rev_e = { to: from, cap: 0, rev: e } e.rev = rev_e this.edges[from].push(e) this.edges[to].push(rev_e) this.edges2[from].push(e) } private bfs(s: number) { this.level.fill(-1) let q = [s] this.level[s] = 0 let level = 1 while (!q.isEmpty()) { const qq: number[] = [] while (!q.isEmpty()) { const v = q.pop()! for (const e of this.edges[v]) { if (e.cap > 0 && this.level[e.to] < 0) { this.level[e.to] = level qq.push(e.to) } } } q = qq level++ } } private dfs(s: number, t: number) { const iter: number[] = Array(this.V).fill(0) type Info = { v: number; f: number; rf: number; parent?: Info; edge?: Edge } const stack: Info[] = [{ v: s, f: Infinity, rf: 0 }] while (!stack.isEmpty()) { const top = stack.last()! if (top.v === t) { stack.pop() top.parent!.rf += top.f top.edge!.cap -= top.f top.edge!.rev.cap += top.f continue } let hasEdge = false const f = top.f - top.rf if (f > 0) { for (let i = iter[top.v]; i < this.edges[top.v].length; i++) { iter[top.v] = i + 1 const e = this.edges[top.v][i] if (e.cap > 0 && this.level[top.v] < this.level[e.to]) { hasEdge = true stack.push({ v: e.to, f: Math.min(f, e.cap), rf: 0, parent: top, edge: e }) break } } } if (!hasEdge) { if (top.v === s) return top.rf stack.pop() if (top.rf > 0) { top.parent!.rf += top.rf top.edge!.cap -= top.rf top.edge!.rev.cap += top.rf } } } return 0 } maxFlow(s: number, t: number) { let flow = 0 while (true) { this.bfs(s) if (this.level[t] < 0) return flow flow += this.dfs(s, t) } } } function main() { const input = new Input() const H = input.number() const W = input.number() const A: { i: number; j: number }[][] = [...Array(5e5 + 1)].map(() => []) for (let i = 0; i < H; i++) { for (let j = 0; j < W; j++) { const a = input.number() A[a].push({ i, j }) } } let ans = 0 const r_map = array(H, 0) const c_map = array(W, 0) for (let a = 1; a <= 5e5; a++) { if (A[a].isEmpty()) continue const r_set = new Set() const c_set = new Set() for (const { i, j } of A[a]) { r_set.add(i) c_set.add(j) } const R = r_set.size const C = c_set.size const s = 0 const t = 1 const g = new Dinic(R + C + 2) Array.from(r_set).forEach((r, idx) => { r_map[r] = idx g.addEdge(s, 2 + idx, 1) }) Array.from(c_set).forEach((c, idx) => { c_map[c] = idx g.addEdge(2 + R + idx, t, 1) }) for (const { i, j } of A[a]) { g.addEdge(2 + r_map[i], 2 + R + c_map[j], 1) } const f = g.maxFlow(s, t) ans += f } console.log(ans) } main()