Problem:
運河 (Canal)
問題文
JOIG 王国は
H
行
W
列のマス目に区切られた長方形の形をしている．上から
i
行目 (
1 ≦ i ≦ H
)，左から
j
列目 (
1 ≦ j ≦ W
) のマスをマス
(i,j)
と呼ぶ．
各マスには標高と呼ばれる整数が定まっている．マス
(i,j)
の標高は
A
i,j
である．
JOIG 王国では，王国を縦断する運河を建設することにした．運河の建設は，以下のように行われる．
ある整数
k
(
1 ≦ k < W
) を定める．左から
k
列目と
k+1
列目の間に，王国の上端から下端まで縦断する運河を建設する．
運河を横切らず，辺で接している標高が同じマスへの移動を繰り返すことで相互に移動できるマスの集まりをここでは
平地
と呼ぶ．国土を管理しやすくするため，平地の個数ができるだけ少なくなるように運河の建設位置を決めたい．
JOIG 王国の地形の情報が与えられたとき，運河を建設した後の JOIG 王国内の平地の個数としてありうる最小値を求めるプログラムを作成せよ．
制約
H ≧ 1
．
W ≧ 2
．
H × W ≦ 500000
．
1 ≦ A
i,j
≦ 10
9
(
1 ≦ i ≦ H
，
1 ≦ j ≦ W
)．
入力される値はすべて整数である．
入力
入力は以下の形式で与えられる．
H
W
A
1,1
A
1,2
…
A
1,W
A
2,1
A
2,2
…
A
2,W
:
A
H,1
A
H,2
…
A
H,W
出力
運河を建設した後の JOIG 王国内の平地の個数としてありうる最小値を出力せよ．
入出力例
入力例 1
4 4
1 1 1 3
2 2 1 3
2 1 1 3
2 2 2 2
出力例 1
4
k=3
として運河を建設すると，JOIG 王国は以下のように
4
個の平地に分かれる．
マス
(1,1)
，
(1,2)
，
(1,3)
，
(2,3)
，
(3,2)
，
(3,3)
からなる平地
マス
(2,1)
，
(2,2)
，
(3,1)
，
(4,1)
，
(4,2)
，
(4,3)
からなる平地
マス
(1,4)
，
(2,4)
，
(3,4)
からなる平地
マス
(4,4)
からなる平地
JOIG 王国内の平地の個数を
3
個以下にすることはできないので，
4
を出力する．
入力例 2
5 8
1 2 2 5 5 5 5 5
1 1 2 2 5 6 5 6
1 1 1 1 6 6 5 6
1 1 3 1 1 6 7 6
1 4 1 1 1 6 6 6
出力例 2
8
k=1
として運河を建設すると，JOIG 王国は
8
個の平地に分かれる．JOIG 王国内の平地の個数を
7
個以下にすることはできないので，
8
を出力する．
入力例 3
1 6
1 1 2 2 3 3
出力例 3
3
入力例 4
2 10
1 1 1 1 1 3 3 3 3 4
1 2 1 3 3 3 1 1 3 3
出力例 4
6
情報オリンピック日本委員会
作 『日本情報オリンピック第3回女性部門 本選 競技課題』

Submitted solutions:

code_snippet: #include <vector>
#include <string>
#include <deque>
#include <tuple>
#include <climits>
#include <numeric>
#include <iostream>
#include <iomanip>
#include <random>
#include <cfloat>
#include <set>
#include <queue>
#include <cmath>
#include <algorithm>
#include <variant>
#include <bitset>
#include <unordered_map>
#include <array>
#include <cmath>
#include <map>
#include <optional>
#include <cassert>
#include <filesystem>
#include <mutex>
#include <thread>
#include <functional>
#include <fstream>
#include <chrono>
#include <stack>
#include <array>

class UnionFind {
	std::vector<int> vec;
public:
	UnionFind(const int size) : vec(size, -1) {};
	int get(const int a) {
		if (vec[a] < 0) return a;
		return vec[a] = get(vec[a]);
	}
	bool unite(int a, int b) {
		a = get(a);
		b = get(b);
		if (a == b) return false;
		if (vec[a] > vec[b]) std::swap(a, b);
		vec[a] += vec[b];
		vec[b] = a;		
		return true;
	}
};
int main() {
	int height, width; std::cin >> height >> width;
	std::vector<std::vector<int>> state(height, std::vector<int>(width));
	for (auto& line : state) {
		for (auto& a : line) {
			std::cin >> a;
		}
	}
	std::vector<int> forward(width), backward(width);
	UnionFind uftForward(height * width), uftBackward(height * width);
	int count{ 0 };
	for (auto i = 0; i < width - 1; ++i) {
		count += height;
		for (auto h = 1; h < height; ++h) {
			if (state[h][i] == state[h - 1][i]) {
				if (uftForward.unite(h * width + i, (h - 1) * width + i)) {
					count--;
				}
			}
		}
		if (i > 0) {
			for (auto h = 0; h < height; ++h) {
				if (state[h][i - 1] == state[h][i]) {
					if (uftForward.unite(h * width + i - 1, h * width + i)) {
						--count;
					}
				}
			}
		}
		forward[i] = count;
	}
	count = 0;
	for (int i = width - 1; i > 0; --i) {
		count += height;
		for (auto h = 1; h < height; ++h) {
			if (state[h][i] == state[h - 1][i]) {
				if (uftBackward.unite(h * width + i, (h - 1) * width + i)) {
					count--;
				}
			}
		}
		if (i + 1 < width) {
			for (auto h = 0; h < height; ++h) {
				if (state[h][i + 1] == state[h][i]) {
					if (uftBackward.unite(h * width + i + 1, h * width + i)) {
						--count;
					}
				}
			}
		}
		backward[i] = count;
	}
	int result{ height * width };
	for (auto i = 1; i < width; ++i) {
		result = std::min(result, forward[i - 1] + backward[i]);
	}
	std::cout << result << '\n';
}
accuracy: 1
time_ms: 130
memory_kb: 20628
final_rank: 9

code_snippet: #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef vector<ll> vi;
typedef vector<vi> vvi;
#define FOR(i,l,r) for(ll i=l;i<r;i++)
#define REP(i,n) FOR(i,0,n)
#define RFOR(i,l,r) for(ll i=r-1;i>=l;i--)
#define RREP(i,n) RFOR(i,0,n)
#define sz(A) ((ll)A.size())
#define ALL(A) A.begin(),A.end()
struct dsu{
    vector<int>nec;
    dsu(int _N):nec(_N,-1){}
    int leader(int v){
        while(nec[v]>=0)v=nec[v];
        return v;
    }
    int merge(int u,int v){
        u=leader(u);
        v=leader(v);
        if(u==v)return u;
        if(nec[u]<nec[v])swap(u,v);
        nec[v]+=nec[u];
        nec[u]=v;
        return v;
    }
    bool same(int u,int v){
        return leader(u)==leader(v);
    }
};
int main(){
    int H,W;cin>>H>>W;
    vvi A(H,vi(W));
    REP(i,H)REP(j,W)cin>>A[i][j];
    vi X(W-1);
    {
        dsu D(H*W);
        ll g=H;
        REP(i,H-1)if(A[i][0]==A[i+1][0])g--,D.merge(i*W,i*W+W);
        REP(j,W-1){
            X[j]=g;
            g+=H;
            REP(i,H)if(A[i][j]==A[i][j+1])g--,D.merge(i*W+j,i*W+j+1);
            REP(i,H-1)if(A[i][j+1]==A[i+1][j+1]&&!D.same(i*W+j+1,i*W+j+1+W))g--,D.merge(i*W+j+1,i*W+j+1+W);
        }
    }
    {
        dsu D(H*W);
        ll g=H;
        REP(i,H-1)if(A[i][W-1]==A[i+1][W-1])g--,D.merge(i*W+W-1,i*W+W-1+W);
        RREP(j,W-1){
            X[j]+=g;
            g+=H;
            REP(i,H)if(A[i][j]==A[i][j+1])g--,D.merge(i*W+j,i*W+j+1);
            REP(i,H-1)if(A[i][j]==A[i+1][j]&&!D.same(i*W+j,i*W+j+W))g--,D.merge(i*W+j,i*W+j+W);
        }
    }
    cout<<*min_element(ALL(X))<<endl;
    return 0;
}
accuracy: 1
time_ms: 130
memory_kb: 18808
final_rank: 7

code_snippet: #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef vector<ll> vi;
typedef vector<vi> vvi;
#define FOR(i,l,r) for(ll i=l;i<r;i++)
#define REP(i,n) FOR(i,0,n)
#define RFOR(i,l,r) for(ll i=r-1;i>=l;i--)
#define RREP(i,n) RFOR(i,0,n)
#define sz(A) ((ll)A.size())
#define ALL(A) A.begin(),A.end()
struct dsu{
    vector<int>nec;
    dsu(int _N):nec(_N,-1){}
    int leader(int v){
        while(nec[v]>=0)v=nec[v];
        return v;
    }
    int merge(int u,int v){
        u=leader(u);
        v=leader(v);
        if(u==v)return u;
        if(nec[u]<nec[v])swap(u,v);
        nec[v]+=nec[u];
        nec[u]=v;
        return v;
    }
    bool same(int u,int v){
        return leader(u)==leader(v);
    }
};
int main(){
    int H,W;cin>>H>>W;
    vvi A(H,vi(W));
    REP(i,H)REP(j,W)cin>>A[i][j];
    vi X(W-1);
    {
        dsu D(H*W);
        ll g=H;
        REP(i,H-1)if(A[i][0]==A[i+1][0])g--,D.merge(i*W,i*W+W);
        REP(j,W-1){
            X[j]=g;
            g+=H;
            REP(i,H)if(A[i][j]==A[i][j+1])g--,D.merge(i*W+j,i*W+j+1);
            REP(i,H-1)if(A[i][j+1]==A[i+1][j+1]&&!D.same(i*W+j+1,i*W+j+1+W))g--,D.merge(i*W+j+1,i*W+j+1+W);
        }
    }
    {
        dsu D(H*W);
        ll g=H;
        REP(i,H-1)if(A[i][W-1]==A[i+1][W-1])g--,D.merge(i*W-1,i*W+W-1+W);
        RREP(j,W-1){
            X[j]+=g;
            g+=H;
            REP(i,H)if(A[i][j]==A[i][j+1])g--,D.merge(i*W+j,i*W+j+1);
            REP(i,H-1)if(A[i][j]==A[i+1][j]&&!D.same(i*W+j,i*W+j+W))g--,D.merge(i*W+j,i*W+j+W);
        }
    }
    cout<<*min_element(ALL(X))<<endl;
    return 0;
}
accuracy: 0.1910828025477707
time_ms: 120
memory_kb: 13004
final_rank: 15

code_snippet: #pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;

mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
ll myRand(ll B) { return (ull)rng() % B; }

struct UnionFind {
    vector<int> par, num;
    UnionFind(int n) : par(n), num(n, 1) {
        for (int i = 0; i < n; ++i) par[i] = i;
    }
    int find(int v) { return (par[v] == v) ? v : (par[v] = find(par[v])); }
    void unite(int u, int v) {
        u = find(u), v = find(v);
        if (u == v) return;
        if (num[u] < num[v]) swap(u, v);
        num[u] += num[v];
        par[v] = u;
    }
    bool same(int u, int v) { return find(u) == find(v); }
    int size(int v) { return num[find(v)]; }
};

#define BUFFER_SIZE 262144
struct fastI{
    char in[1<<23];
    const char *o;
    fastI(){
        o = in;
        in[fread(in, 1, sizeof(in)-4, stdin)] = 0;
    }
    fastI& operator>>(char& c){
        c = *o++;
        return *this;
    }
    fastI& operator>>(int& u){
        char c;
        for (u = *o++ & 15; (c = *o++) >= '0'; ){
            u = (u << 3) + (u << 1) + (c & 15);
        }
        return *this;
    }
} fin;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    fin >> n >> m;
    vector<vector<int>> a(n, vector<int>(m));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            fin >> a[i][j];
        }
    }
    vector<int> L(m + 1), R(m + 1);
    {
        UnionFind uf(n * m);
        for (int j = 0; j < m; j++) {
            L[j + 1] = L[j] + n;
            int cur = j;
            for (int i = 0; i < n; i++) {
                if (i && a[i][j] == a[i - 1][j] && !uf.same(cur, cur - m)) {
                    uf.unite(cur, cur - m);
                    L[j + 1] -= 1;
                }
                if (j && a[i][j] == a[i][j - 1] && !uf.same(cur, cur - 1)) {
                    uf.unite(cur, cur - 1);
                    L[j + 1] -= 1;
                }
                cur += m;
            }
        }
    }
    {
        UnionFind uf(n * m);
        for (int jj = 0; jj < m; jj++) {
            int j = m - 1 - jj;
            R[jj + 1] = R[jj] + n;
            int cur = j;
            for (int i = 0; i < n; i++) {
                if (i && a[i][j] == a[i - 1][j] && !uf.same(cur, cur - m)) {
                    uf.unite(cur, cur - m);
                    R[jj + 1] -= 1;
                }
                if (j < m - 1 && a[i][j] == a[i][j + 1] && !uf.same(cur, cur + 1)) {
                    uf.unite(cur, cur + 1);
                    R[jj + 1] -= 1;
                }
                cur += m;
            }
        }
    }
    int res = n * m;
    for (int i = 1; i < m; i++) {
        res = min(res, L[i] + R[m - i]);
    }
    cout << res << endl;
    return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 26856
final_rank: 4

code_snippet: #pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;

mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
ll myRand(ll B) { return (ull)rng() % B; }

struct UnionFind {
    vector<int> par, num;
    UnionFind(int n) : par(n), num(n, 1) {
        iota(par.begin(), par.end(), 0); // include<numeric>
    }
    int find(int v) { return (par[v] == v) ? v : (par[v] = find(par[v])); }
    void unite(int u, int v) {
        u = find(u), v = find(v);
        if (u == v) return;
        if (num[u] < num[v]) swap(u, v);
        num[u] += num[v];
        par[v] = u;
    }
    bool same(int u, int v) { return find(u) == find(v); }
    int size(int v) { return num[find(v)]; }
};

#define BUFFER_SIZE 262144
struct fastI{
    char in[1<<23];
    char const*o;
    fastI(){
        o = in;
        in[fread(in, 1, sizeof(in)-4, stdin)] = 0;
    }
    fastI& operator>>(char& c){
        c = *o++;
        return *this;
    }
    fastI& operator>>(int& u){
        char c;
        for (u = *o++ & 15; (c = *o++) >= '0'; ){
            u = (u << 3) + (u << 1) + (c & 15);
        }
        return *this;
    }
} fin;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    fin >> n >> m;
    vector<vector<int>> a(n, vector<int>(m));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            fin >> a[i][j];
        }
    }
    vector<int> L(m + 1), R(m + 1);
    {
        UnionFind uf(n * m);
        for (int j = 0; j < m; j++) {
            L[j + 1] = L[j] + n;
            int cur = j;
            for (int i = 0; i < n; i++) {
                if (i and a[i][j] == a[i - 1][j] and !uf.same(cur, cur - m)) {
                    uf.unite(cur, cur - m);
                    L[j + 1] -= 1;
                }
                if (j and a[i][j] == a[i][j - 1] and !uf.same(cur, cur - 1)) {
                    uf.unite(cur, cur - 1);
                    L[j + 1] -= 1;
                }
                cur += m;
            }
        }
    }
    {
        UnionFind uf(n * m);
        for (int jj = 0; jj < m; jj++) {
            int j = m - 1 - jj;
            R[jj + 1] = R[jj] + n;
            int cur = j;
            for (int i = 0; i < n; i++) {
                if (i and a[i][j] == a[i - 1][j] and !uf.same(cur, cur - m)) {
                    uf.unite(cur, cur - m);
                    R[jj + 1] -= 1;
                }
                if (j < m - 1 and a[i][j] == a[i][j + 1] and !uf.same(cur, cur + 1)) {
                    uf.unite(cur, cur + 1);
                    R[jj + 1] -= 1;
                }
                cur += m;
            }
        }
    }
    int res = n * m;
    for (int i = 1; i < m; i++) {
        res = min(res, L[i] + R[m - i]);
    }
    cout << res << endl;
}
accuracy: 1
time_ms: 20
memory_kb: 26868
final_rank: 5

code_snippet: #line 1 "/opt/library/template.hpp"
#include <bits/stdc++.h>
using namespace std;

using ll = long long;
using i64 = long long;
using u32 = unsigned int;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using f128 = __float128;

template <class T>
constexpr T infty = 0;
template <>
constexpr int infty<int> = 1'000'001'000;
template <>
constexpr ll infty<ll> = ll(infty<int>) * infty<int> * 2;
template <>
constexpr u32 infty<u32> = infty<int>;
template <>
constexpr u64 infty<u64> = infty<ll>;
template <>
constexpr i128 infty<i128> = i128(infty<ll>) * infty<ll>;
template <>
constexpr double infty<double> = infty<ll>;
template <>
constexpr long double infty<long double> = infty<ll>;
#define inf infty<ll>

using pi = pair<ll, ll>;
using vi = vector<ll>;
using vvi = vector<vector<ll>>;
template <class T>
using vc = vector<T>;
template <class T>
using vvc = vector<vc<T>>;
template <class T>
using vvvc = vector<vvc<T>>;
template <class T>
using vvvvc = vector<vvvc<T>>;
template <class T>
using vvvvvc = vector<vvvvc<T>>;
template <class T>
using pq = priority_queue<T>;
template <class T>
using pqg = priority_queue<T, vector<T>, greater<T>>;

#define vv(type, name, h, ...) \
  vector<vector<type>> name(h, vector<type>(__VA_ARGS__))
#define vvv(type, name, h, w, ...)   \
  vector<vector<vector<type>>> name( \
      h, vector<vector<type>>(w, vector<type>(__VA_ARGS__)))
#define vvvv(type, name, a, b, c, ...)       \
  vector<vector<vector<vector<type>>>> name( \
      a, vector<vector<vector<type>>>(       \
             b, vector<vector<type>>(c, vector<type>(__VA_ARGS__))))

#define rep1(a) for (ll _ = 0; _ < (ll)(a); ++_)
#define rep2(i, a) for (ll i = 0; i < (ll)(a); ++i)
#define rep3(i, a, b) for (ll i = a; i < (ll)(b); ++i)
#define rep4(i, a, b, c) for (ll i = a; i < (ll)(b); i += (c))
#define rrep1(a) for (ll i = (a)-1; i >= (ll)(0); --i)
#define rrep2(i, a) for (ll i = (a)-1; i >= (ll)(0); --i)
#define rrep3(i, a, b) for (ll i = (b)-1; i >= (ll)(a); --i)
#define rrep4(i, a, b, c) for (ll i = (b)-1; i >= (ll)(a); i -= (c))
#define overload4(a, b, c, d, e, ...) e
#define rep(...) overload4(__VA_ARGS__, rep4, rep3, rep2, rep1)(__VA_ARGS__)
#define rrep(...) overload4(__VA_ARGS__, rrep4, rrep3, rrep2, rrep1)(__VA_ARGS__)

#define all(x) (x).begin(),(x).end()
#define len(x) (ll)(x.size())
#define elif else if
#define bit(x, i) (((x)>>(i))&1)

#define eb emplace_back
#define mp make_pair
#define mt make_tuple
#define fi first
#define se second

#define stoi stoll
#define abs llabs

#define MIN(v) *min_element(all(v))
#define MAX(v) *max_element(all(v))
#define LB(c, x) distance((c).begin(), lower_bound(all(c), (x)))
#define UB(c, x) distance((c).begin(), upper_bound(all(c), (x)))
#define UNIQUE(x) \
  sort(all(x)), x.erase(unique(all(x)), x.end()), x.shrink_to_fit()

ll popcnt(ll x) { return __builtin_popcountll(x); }
ll popcnt(u64 x) { return __builtin_popcountll(x); }
ll popcnt_mod_2(ll x) { return __builtin_parityll(x); }
ll popcnt_mod_2(u64 x) { return __builtin_parityll(x); }
ll topbit(ll x) { return (x == 0 ? -1 : 63 - __builtin_clzll(x)); }
ll topbit(u64 x) { return (x == 0 ? -1 : 63 - __builtin_clzll(x)); }
ll lowbit(ll x) { return (x == 0 ? -1 : __builtin_ctzll(x)); }
ll lowbit(u64 x) { return (x == 0 ? -1 : __builtin_ctzll(x)); }

template <typename T>
T floor(T a, T b) {
  return a / b - (a % b && (a ^ b) < 0);
}
template <typename T>
T ceil(T x, T y) {
  return floor(x + y - 1, y);
}
template <typename T>
T bmod(T x, T y) {
  return x - y * floor(x, y);
}
template <typename T>
pair<T, T> divmod(T x, T y) {
  T q = floor(x, y);
  return {q, x - q * y};
}

template<typename T> T SUM(const vector<T> &A) {
  T s = 0; for (auto &&a: A) s += a;
  return s;
}

template <typename T>
T POP(queue<T> &que) {
  T a = que.front();
  que.pop();
  return a;
}
template <typename T>
T POP(deque<T> &que) {
  T a = que.front();
  que.pop_front();
  return a;
}
template <typename T>
T POP(pq<T> &que) {
  T a = que.top();
  que.pop();
  return a;
}
template <typename T>
T POP(pqg<T> &que) {
  T a = que.top();
  que.pop();
  return a;
}
template <typename T>
T POP(vc<T> &que) {
  T a = que.back();
  que.pop_back();
  return a;
}

template <typename F>
ll binary_search(F check, ll ok, ll ng, bool check_ok = true) {
  if (check_ok) assert(check(ok));
  while (abs(ok - ng) > 1) {
    auto x = (ng + ok) / 2;
    (check(x) ? ok : ng) = x;
  }
  return ok;
}
template <typename F>
f128 binary_search_real(F check, f128 ok, f128 ng, ll iter = 100) {
  rep(iter) {
    f128 x = (ok + ng) / 2;
    (check(x) ? ok : ng) = x;
  }
  return (ok + ng) / 2;
}

// ? は -1
vc<int> s_to_vi(const string &S, char first_char) {
  vc<int> A(S.size());
  rep(i, S.size()) { A[i] = (S[i] != '?' ? S[i] - first_char : -1); }
  return A;
}

template <typename T, typename U>
vc<T> cumsum(vc<U> &A, ll off = 1) {
  ll N = A.size();
  vc<T> B(N + 1);
  rep(i, N) { B[i + 1] = B[i] + A[i]; }
  if (off == 0) B.erase(B.begin());
  return B;
}

// stable sort
template <typename T>
vi argsort(const vector<T> &A) {
  vi ids(len(A));
  iota(all(ids), 0);
  sort(all(ids),
       [&](ll i, ll j) { return (A[i] == A[j] ? i < j : A[i] < A[j]); });
  return ids;
}

// A[I[0]], A[I[1]], ...
template <typename T>
vc<T> rearrange(const vc<T> &A, const vi &I) {
  vc<T> B(len(I));
  rep(i, len(I)) B[i] = A[I[i]];
  return B;
}

template<typename T> inline bool chmax(T &a, T b) {return ((a<b)?(a=b,true):(false));}
template<typename T> inline bool chmin(T &a, T b) {return ((a>b)?(a=b,true):(false));}

inline void wt(const char c) { cout << c; }
inline void wt(const string s) { cout << s; }
inline void wt(const char *s) { cout << s; }

template <typename T>
void wt_integer(T x) {
  if (x < 0) { cout << (i64)(x); }
  else { cout << (u64)(x); }
}
template <typename T>
void wt_real(T x) {
  cout << fixed << setprecision(15) << (long double)(x);
}

inline void wt(int x) { wt_integer(x); }
inline void wt(ll x) { wt_integer(x); }
inline void wt(i128 x) { wt_integer(x); }
inline void wt(u32 x) { wt_integer(x); }
inline void wt(u64 x) { wt_integer(x); }
inline void wt(u128 x) { wt_integer(x); }
inline void wt(double x) { wt_real(x); }
inline void wt(long double x) { wt_real(x); }
inline void wt(f128 x) { wt_real(x); }

template <class T, class U>
void wt(const pair<T, U> val) {
  wt(val.first); wt(' '); wt(val.second);
}
template <size_t N = 0, typename T>
void wt_tuple(const T t) {
  if constexpr (N < std::tuple_size<T>::value) {
    if constexpr (N > 0) { wt(' '); }
    const auto x = std::get<N>(t);
    wt(x);
    wt_tuple<N + 1>(t);
  }
}
template <class... T>
void wt(tuple<T...> tpl) {
  wt_tuple(tpl);
}
template <class T, size_t S>
void wt(const array<T, S> val) {
  auto n = val.size();
  for (size_t i = 0; i < n; i++) {
    if (i) wt(' ');
    wt(val[i]);
  }
}
template <class T>
void wt(const vector<T> val) {
  auto n = val.size();
  for (size_t i = 0; i < n; i++) {
    if (i) wt(' ');
    wt(val[i]);
  }
}

void print() { wt('\n'); }
template <class Head, class... Tail>
void print(Head &&head, Tail &&... tail) {
  wt(head);
  if (sizeof...(Tail)) wt(' ');
  print(forward<Tail>(tail)...);
}

void YES(bool t = 1) { print(t ? "YES" : "NO"); }
void NO(bool t = 1) { YES(!t); }
void Yes(bool t = 1) { print(t ? "Yes" : "No"); }
void No(bool t = 1) { Yes(!t); }
void yes(bool t = 1) { print(t ? "yes" : "no"); }
void no(bool t = 1) { yes(!t); }
void onez(bool t = 1) { print(t ? 1 : 0); }
#define endl '\n'
#define dump(x) {cerr << #x " = " << x << '\n';}
#line 2 "/opt/library/ds/unionfind/unionfind.hpp"

struct UnionFind {
  ll n, n_comp;
  vi dat; // parent or (-size)
  UnionFind(ll n = 0) { build(n); }

  void build(ll m) {
    n = m, n_comp = m;
    dat.assign(n, -1);
  }

  void reset() { build(n); }

  ll operator[](ll x) {
    while (dat[x] >= 0) {
      ll parent = dat[dat[x]];
      if (parent < 0) { return dat[x]; }
      x = dat[x] = parent;
    }
    return x;
  }

  ll size(ll x) {
    x = (*this)[x];
    return -dat[x];
  }

  bool merge(ll x, ll y) {
    x = (*this)[x], y = (*this)[y];
    if (x == y) return false;
    if (-dat[x] < -dat[y]) swap(x, y);
    dat[x] += dat[y], dat[y] = x, n_comp--;
    return true;
  }
};
#line 3 "main.cpp"

int solve();
int main() {
  cin.tie(nullptr);
  ios_base::sync_with_stdio(false);
  while (!solve()) {break;}
  return 0;
}

int solve() {
  ll H, W;
  cin >> H >> W;
  vv(ll, A, H, W);
  rep(i, H) rep(j, W) cin >> A[i][j];
  vi L(W+1);
  L[0] = 0;
  UnionFind uf(H*W);
  rep(j, W) {
    L[j+1] = L[j] + H;
    if (j > 0) {
      rep(i, H) {
        if (A[i][j] == A[i][j-1]) {
          uf.merge(j*H+i, (j-1)*H+i);
          L[j+1]--;
        }
      }
    }
    rep(i, H-1) {
      if (A[i][j] == A[i+1][j]) {
        if (uf[j*H+i] != uf[j*H+i+1]) {
          uf.merge(j*H+i, j*H+i+1);
          L[j+1]--;
        }
      }
    }
  }
  vi R(W+1);
  R[W] = 0;
  UnionFind uf2(H*W);
  rrep(j, W) {
    R[j] = R[j+1] + H;
    if (j < W-1) {
      rep(i, H) {
        if (A[i][j] == A[i][j+1]) {
          uf2.merge(j*H+i, (j+1)*H+i);
          R[j]--;
        }
      }
    }
    rep(i, H-1) {
      if (A[i][j] == A[i+1][j]) {
        if (uf2[j*H+i] != uf2[j*H+i+1]) {
          uf2.merge(j*H+i, j*H+i+1);
          R[j]--;
        }
      }
    }
  }
  ll ans = H*W;
  rep(i, 1, W) chmin(ans, L[i]+R[i]);
  print(ans);
  return 0;
}
accuracy: 1
time_ms: 50
memory_kb: 24728
final_rank: 6

code_snippet: #line 1 "a.cpp"
#define PROBLEM ""
#line 2 "/home/kuhaku/home/github/algo/lib/template/template.hpp"
#pragma GCC target("sse4.2,avx2,bmi2")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
template <class T, class U>
constexpr bool chmax(T &a, const U &b) {
    return a < (T)b ? a = (T)b, true : false;
}
template <class T, class U>
constexpr bool chmin(T &a, const U &b) {
    return (T)b < a ? a = (T)b, true : false;
}
constexpr std::int64_t INF = 1000000000000000003;
constexpr int Inf = 1000000003;
constexpr double EPS = 1e-7;
constexpr double PI = M_PI;
#line 3 "/home/kuhaku/home/github/algo/lib/template/macro.hpp"
#define FOR(i, m, n) for (int i = (m); i < int(n); ++i)
#define FORR(i, m, n) for (int i = (m)-1; i >= int(n); --i)
#define FORL(i, m, n) for (int64_t i = (m); i < int64_t(n); ++i)
#define rep(i, n) FOR (i, 0, n)
#define repn(i, n) FOR (i, 1, n + 1)
#define repr(i, n) FORR (i, n, 0)
#define repnr(i, n) FORR (i, n + 1, 1)
#define all(s) (s).begin(), (s).end()
#line 3 "/home/kuhaku/home/github/algo/lib/template/sonic.hpp"
struct Sonic {
    Sonic() {
        std::ios::sync_with_stdio(false);
        std::cin.tie(nullptr);
        std::cout << std::fixed << std::setprecision(20);
    }

    constexpr void operator()() const {}
} sonic;
#line 5 "/home/kuhaku/home/github/algo/lib/template/atcoder.hpp"
using namespace std;
using ll = std::int64_t;
using ld = long double;
template <class T, class U>
std::istream &operator>>(std::istream &is, std::pair<T, U> &p) {
    return is >> p.first >> p.second;
}
template <class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {
    for (T &i : v) is >> i;
    return is;
}
template <class T, class U>
std::ostream &operator<<(std::ostream &os, const std::pair<T, U> &p) {
    return os << '(' << p.first << ',' << p.second << ')';
}
template <class T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {
    for (auto it = v.begin(); it != v.end(); ++it) {
        os << (it == v.begin() ? "" : " ") << *it;
    }
    return os;
}
template <class Head, class... Tail>
void co(Head &&head, Tail &&...tail) {
    if constexpr (sizeof...(tail) == 0) std::cout << head << '\n';
    else std::cout << head << ' ', co(std::forward<Tail>(tail)...);
}
template <class Head, class... Tail>
void ce(Head &&head, Tail &&...tail) {
    if constexpr (sizeof...(tail) == 0) std::cerr << head << '\n';
    else std::cerr << head << ' ', ce(std::forward<Tail>(tail)...);
}
template <typename T, typename... Args>
auto make_vector(T x, int arg, Args... args) {
    if constexpr (sizeof...(args) == 0) return std::vector<T>(arg, x);
    else return std::vector(arg, make_vector<T>(x, args...));
}
void Yes(bool is_correct = true) {
    std::cout << (is_correct ? "Yes" : "No") << '\n';
}
void No(bool is_not_correct = true) {
    Yes(!is_not_correct);
}
void YES(bool is_correct = true) {
    std::cout << (is_correct ? "YES" : "NO") << '\n';
}
void NO(bool is_not_correct = true) {
    YES(!is_not_correct);
}
void Takahashi(bool is_correct = true) {
    std::cout << (is_correct ? "Takahashi" : "Aoki") << '\n';
}
void Aoki(bool is_not_correct = true) {
    Takahashi(!is_not_correct);
}
#line 3 "/home/kuhaku/home/github/algo/lib/tree/union_find.hpp"

/**
 * @brief 素集合データ構造
 * @details Implement (union by size) + (path compression)
 * @see https://github.com/atcoder/ac-library/blob/master/atcoder/dsu.hpp
 */
struct union_find {
    union_find() = default;
    explicit union_find(int _n) : data(_n, -1) {}

    int root(int x) { return data[x] < 0 ? x : data[x] = root(data[x]); }
    int get_root(int x) { return root(x); }

    bool is_root(int x) const { return data[x] < 0; }

    bool same(int x, int y) { return root(x) == root(y); }
    bool is_same(int x, int y) { return same(x, y); }

    int size(int x) { return -(data[root(x)]); }
    int get_size(int x) { return size(x); }

    std::vector<int> leaders() {
        std::vector<int> res;
        for (int i = 0; i < (int)data.size(); ++i) {
            if (is_root(i)) res.emplace_back(i);
        }
        return res;
    }

    bool unite(int x, int y) {
        x = root(x), y = root(y);
        if (x == y) return false;
        if (data[x] > data[y]) std::swap(x, y);
        data[x] += data[y];
        data[y] = x;
        return true;
    }

    template <class F>
    bool unite(int x, int y, F f) {
        x = root(x), y = root(y);
        if (x != y) {
            if (data[x] > data[y]) std::swap(x, y);
            data[x] += data[y];
            data[y] = x;
        }
        f(x, y);
        return x != y;
    }

  private:
    std::vector<int> data;
};
#line 4 "a.cpp"

int main(void) {
    int h, w;
    cin >> h >> w;
    vector a(h, vector(w, 0));
    cin >> a;

    auto in_field = [&](int x, int y) {
        return 0 <= x && x < h && 0 <= y && y < w;
    };
    union_find uf(h * w);
    vector<int> dx = {1, 0}, dy = {0, -1};
    int sz = 0;
    vector<int> ans(w - 1);
    rep (y, w - 1) {
        sz += h;
        rep (x, h) {
            rep (k, 2) {
                int nx = x + dx[k], ny = y + dy[k];
                if (!in_field(nx, ny))
                    continue;
                if (a[x][y] == a[nx][ny])
                    sz -= uf.unite(x * w + y, nx * w + ny);
            }
        }
        ans[y] += sz;
    }

    sz = 0;
    dy = {0, 1};
    uf = union_find(h * w);
    for (int y = w - 1; y >= 1; --y) {
        sz += h;
        rep (x, h) {
            rep (k, 2) {
                int nx = x + dx[k], ny = y + dy[k];
                if (!in_field(nx, ny))
                    continue;
                if (a[x][y] == a[nx][ny])
                    sz -= uf.unite(x * w + y, nx * w + ny);
            }
        }
        ans[y - 1] += sz;
    }

    co(*min_element(all(ans)));

    return 0;
}
accuracy: 1
time_ms: 50
memory_kb: 20588
final_rank: 1

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

struct UnionFind {
public:
	int N;
	vector<int> par;
	UnionFind() : N(0) {}
	explicit UnionFind(int n) : N(n), par(n, -1) {}
	int leader(int a) {
		if (par[a] < 0) return a;
		return par[a] = leader(par[a]);
	}
	void merge(int a, int b) {
		int x = leader(a), y = leader(b);
		if (x == y) return;
		if (-par[x] < -par[y]) swap(x, y);
		par[x] += par[y];
		par[y] = x;
	}
	bool same(int a, int b) {
		return leader(a) == leader(b);
	}
	int size(int a) {
		return -par[leader(a)];
	}
};

int main() {
  int h, w;
  cin >> h >> w;
  vector<vector<int>> a(h, vector<int>(w));
  for (int i = 0; i < h; i++)
    for (int j = 0; j < w; j++)
      cin >> a[i][j];
  vector<int> l(w + 1);
  l[0] = 0;
  UnionFind uf1(h * w);
  for (int i = 0; i < w; i++) {
    l[i + 1] = l[i] + h;
    if (i)
      for (int j = 0; j < h; j++)
        if (a[j][i] == a[j][i - 1]) {
          uf1.merge(i * h + j, (i - 1) * h + j);
          l[i + 1]--;
        }
    for (int j = 0; j < h - 1; j++)
      if (a[j][i] == a[j + 1][i])
        if (!uf1.same(i * h + j, i * h + j + 1)) {
          uf1.merge(i * h + j, i * h + j + 1);
          l[i + 1]--;
        }
  }
  vector<int> r(w + 1);
  r[w] = 0;
  UnionFind uf2(h * w);
  for (int i = w - 1; i >= 0; i--) {
    r[i] = r[i + 1] + h;
    if (i < w - 1)
      for (int j = 0; j < h; j++)
        if (a[j][i] == a[j][i + 1]) {
          uf2.merge(i * h + j, (i + 1) * h + j);
          r[i]--;
        }
    for (int j = 0; j < h - 1; j++)
      if (a[j][i] == a[j + 1][i])
        if (!uf2.same(i * h + j, i * h + j + 1)) {
          uf2.merge(i * h + j, i * h + j + 1);
          r[i]--;
        }
  }
  int ans = h * w;
  for (int i = 1; i < w; i++)
    ans = min(ans, l[i] + r[i]);
  cout << ans << '\n';
}
accuracy: 1
time_ms: 130
memory_kb: 20636
final_rank: 10

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for(int i=0;i<n;i++)
#define all(A) A.begin(),A.end()
using ll = long long;
using vll = vector<ll>;
using vvll = vector<vll>;

struct dsu {
  public:
    dsu() : _n(0) {}
    dsu(int n) : _n(n), par_size(n, -1) {}

    int merge(int a, int b) {
        int x = leader(a), y = leader(b);
        if (x == y) return x;
        if (-par_size[x] < -par_size[y]) swap(x, y);
        par_size[x] += par_size[y];
        par_size[y] = x;
        return x;
    }
    int merge(pair<ll,ll> p) {
        int a=p.first;
        int b=p.second;
        int x = leader(a), y = leader(b);
        if (x == y) return x;
        if (-par_size[x] < -par_size[y]) swap(x, y);
        par_size[x] += par_size[y];
        par_size[y] = x;
        return x;
    }

    bool same(int a, int b) {
        return leader(a) == leader(b);
    }
    bool same(pair<ll,ll> p) {
        int a=p.first;
        int b=p.second;
        return leader(a) == leader(b);
    }

    int leader(int a) {
        if (par_size[a] < 0) return a;
        return par_size[a] = leader(par_size[a]);
    }

    int size(int a) {
        return -par_size[leader(a)];
    }

    vector<vector<int>> groups() {
        std::vector<int> leader_buf(_n), gr_size(_n);
        for (int i = 0; i < _n; i++) {
            leader_buf[i] = leader(i);
            gr_size[leader_buf[i]]++;
        }
        std::vector<vector<int>> res(_n);
        for (int i = 0; i < _n; i++) {
            res[i].reserve(gr_size[i]);
        }
        for (int i = 0; i < _n; i++) {
            res[leader_buf[i]].push_back(i);
        }
        res.erase(
            remove_if(res.begin(), res.end(),
                           [&](const vector<int>& v) { return v.empty(); }),
            res.end());
        return res;
    }
  private:
    int _n;
    vector<int> par_size;
};

vll dx={-1,0,1,0};
vll dy={0,1,0,-1};
int main() {
    ll H,W;
    cin>>H>>W;
    vvll A(H,vll(W));
    rep(h,H)rep(w,W)cin>>A[h][w];
    vvll L(2,vll(W-1,0));
    rep(k,2){
        dsu D(H*W);
        ll res=H;
        rep(h,H-1){
            if(A[h][0]==A[h+1][0]){
                D.merge(h*W,(h+1)*W);
                res--;
            }
        }
        L[k][0]=res;
        rep(w,W-2){
            res+=H;
            rep(h,H){
                if(A[h][w]==A[h][w+1]){
                    res--;
                    D.merge(h*W+w,h*W+w+1);
                }
            }
            rep(h,H-1){
                if(A[h][w+1]==A[h+1][w+1]){
                    if(!D.same(h*W+w+1,(h+1)*W+w+1)){
                        res--;
                        D.merge(h*W+w+1,(h+1)*W+w+1);
                    }
                }
            }
            L[k][w+1]=res;
        }
        rep(h,H)reverse(all(A[h]));
    }
    reverse(all(L[1]));
    ll an=1e18;
    rep(w,W-1)an=min(an,L[0][w]+L[1][w]);
    cout<<an<<endl;
}
accuracy: 1
time_ms: 130
memory_kb: 18812
final_rank: 8

code_snippet: #define rep(i,n) for(int i=0;i<(int)(n);i++)
#define ALL(v) v.begin(),v.end()
typedef long long ll;
 
#include<bits/stdc++.h>
using namespace std;

class Dis{
  public:
  vector<ll> rank,p,siz;
  
  Dis(int s){
    rank.resize(s,0);
    p.resize(s,0);
    siz.resize(s,1);
    rep(i,s) makeSet(i);
  }
  
  void makeSet(int x){
    p[x]=x;
    rank[x]=0;
  }
  
  bool same(int x,int y){
    return root(x)==root(y);
  }
  
  void unite(int x,int y){
    if(same(x,y)) return;
    link(root(x),root(y));
  }
  
  void link(int x,int y){
    if(rank[x]>rank[y]){
      p[y]=x;
      siz[x]+=siz[y];
    }
    else{
      p[x]=y;
      siz[y]+=siz[x];
      if(rank[x]==rank[y]) rank[y]++;
    }
  }
  
  int root(int x){
    if(x != p[x]) p[x]=root(p[x]);
    return p[x];
  }
  
  int size(int x){
    return siz[root(x)];
  }
};
 
int main(){
  ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  
  int h,w;
  cin>>h>>w;
  vector<vector<int>> A(h,vector<int>(w)),L(h,vector<int>(w)),R(h,vector<int>(w));
  rep(i,h) rep(j,w) cin>>A[i][j];
  Dis dsl=Dis(h*w),dsr=Dis(h*w);
  
  auto f=[&](int i,int j){
    return i*w+j;
  };
  
  rep(j,w){
    rep(i,h){
      if(i<h-1 && A[i][j]==A[i+1][j]) dsl.unite(f(i,j),f(i+1,j));
      if(j>0 && A[i][j]==A[i][j-1]) dsl.unite(f(i,j),f(i,j-1));
    }
    rep(i,h) L[i][j]=dsl.root(f(i,j));
  }
  for(int j=w-1;j>=0;j--){
    rep(i,h){
      if(i<h-1 && A[i][j]==A[i+1][j]) dsr.unite(f(i,j),f(i+1,j));
      if(j<w-1 && A[i][j]==A[i][j+1]) dsr.unite(f(i,j),f(i,j+1));
    }
    rep(i,h) R[i][j]=dsr.root(f(i,j));
  }
  
  int cnt=0;
  rep(i,h) rep(j,w){
    if(dsl.root(f(i,j))==f(i,j)) cnt++;
  }
  
  int mi=h+1;
  rep(j,w-1){
    set<pair<int,int>> s;
    rep(i,h){
      if(A[i][j]==A[i][j+1]) s.insert({L[i][j],R[i][j+1]});
    }
    mi=min(mi,(int)s.size());
  }
  cout<<cnt+mi<<endl;

  return 0;
}
accuracy: 1
time_ms: 110
memory_kb: 79260
final_rank: 11

code_snippet: #define rep(i,n) for(int i=0;i<(int)(n);i++)
#define ALL(v) v.begin(),v.end()
typedef long long ll;
 
#include<bits/stdc++.h>
using namespace std;

class Dis{
  public:
  vector<ll> rank,p,siz;
  
  Dis(int s){
    rank.resize(s,0);
    p.resize(s,0);
    siz.resize(s,1);
    rep(i,s) makeSet(i);
  }
  
  void makeSet(int x){
    p[x]=x;
    rank[x]=0;
  }
  
  bool same(int x,int y){
    return root(x)==root(y);
  }
  
  void unite(int x,int y){
    if(same(x,y)) return;
    link(root(x),root(y));
  }
  
  void link(int x,int y){
    if(rank[x]>rank[y]){
      p[y]=x;
      siz[x]+=siz[y];
    }
    else{
      p[x]=y;
      siz[y]+=siz[x];
      if(rank[x]==rank[y]) rank[y]++;
    }
  }
  
  int root(int x){
    if(x != p[x]) p[x]=root(p[x]);
    return p[x];
  }
  
  int size(int x){
    return siz[root(x)];
  }
};
 
int main(){
  ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  
  int h,w;
  cin>>h>>w;
  vector<vector<int>> A(h,vector<int>(w));
  rep(i,h) rep(j,w) cin>>A[i][j];
  Dis ds=Dis(h*w);
  
  auto f=[&](int i,int j){
    return i*w+j;
  };
  
  rep(i,h) rep(j,w){
    if(i<h-1 && A[i][j]==A[i+1][j]) ds.unite(f(i,j),f(i+1,j));
    if(j<w-1 && A[i][j]==A[i][j+1]) ds.unite(f(i,j),f(i,j+1));
  }
  int cnt=0;
  rep(i,h) rep(j,w){
    if(ds.root(f(i,j))==f(i,j)) cnt++;
  }
  int mi=w+1;
  rep(j,w-1){
    set<int> s;
    rep(i,h){
      if(A[i][j]==A[i][j+1]) s.insert(ds.root(f(i,j)));
    }
    mi=min(mi,(int)s.size());
  }
  cout<<cnt+mi<<endl;

  return 0;
}
accuracy: 0.3885350318471338
time_ms: 40
memory_kb: 16880
final_rank: 14

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

using namespace std;

#define rep(i,n) for(int i=0;i<(n);++i)
#define reps(i,n) for(int i=1;i<=(n);++i)
#define all(x) begin(x),end(x)
#define Fixed fixed << setprecision(12)
#define updiv(a, b) (((a) + (b) - 1) / (b))
using pii = pair<int,int>;
constexpr int32_t INF  = 0x3f3f3f3f;
constexpr int_fast64_t LINF = 0x3f3f3f3f3f3f3f3fLL;
constexpr int mod1 = 1e9+7;
constexpr int mod2 = 998244353;

template <class Func>
class FixPoint : Func {
public:
    explicit constexpr FixPoint(Func&& f) noexcept : Func(forward<Func>(f)) {}

    template <class... Args>
    constexpr decltype(auto) operator()(Args&&... args) const {
        return Func::operator()(*this, std::forward<Args>(args)...);
    }
};

template <class Func>
static inline constexpr decltype(auto) makeFixPoint(Func&& f) noexcept {
    return FixPoint<Func>{forward<Func>(f)};
}

template<class T, class U> istream& operator>>(istream& is, pair<T, U>& p){ is >> p.first >> p.second; return (is); }
template<class T, class U> ostream& operator<<(ostream& os, pair<T, U>& p){ os << p.first << ' ' << p.second; return (os); }
template<class T> istream& operator>>(istream& is, vector<T>& v){ for (auto &&e : v) is >> e; return (is); }
template<class T> ostream& operator<<(ostream& os, vector<T>& v){ for (auto &&e : v) os << e << ' '; return (os); }

template<class T> inline const T& max(const vector<T> &v) { return *max_element(v.begin(), v.end()); }
template<class T> inline const T& min(const vector<T> &v) { return *min_element(v.begin(), v.end()); }
template <class A, class B> inline bool chmax(A &a, const B &b) { return b > a && (a = b, true); }
template <class A, class B> inline bool chmin(A &a, const B &b) { return b < a && (a = b, true); }

template <class T> using max_heap = priority_queue<T>;
template <class T> using min_heap = priority_queue<T, vector<T>, greater<T> >;
template <class A, class B> using umap = unordered_map<A, B>;
template <class T> using uset = unordered_set<T>;

template <class T> using Max_Heap = __gnu_pbds::priority_queue<T, less<T>, __gnu_pbds::rc_binomial_heap_tag>;
template <class T> using Min_Heap = __gnu_pbds::priority_queue<T, greater<T>, __gnu_pbds::rc_binomial_heap_tag>;
template <class T> using Set = __gnu_pbds::tree<T, __gnu_pbds::null_type, less<T>, __gnu_pbds::rb_tree_tag, __gnu_pbds::tree_order_statistics_node_update>;
template <class K, class V> using Umap = __gnu_pbds::gp_hash_table<K, V>;
template <class T> using Rope = __gnu_cxx::rope<T>;
using Trie = __gnu_pbds::trie<string, __gnu_pbds::null_type, __gnu_pbds::trie_string_access_traits<>, __gnu_pbds::pat_trie_tag, __gnu_pbds::trie_prefix_search_node_update>;

template <class T> void bye(T x){ cout << x << '\n'; exit(0); }

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

struct UnionFind{
    int n;
    vector<int> data;

    UnionFind(int sz) : n(sz), data(sz, -1){}

    bool unite(int x, int y){
        x = find(x), y = find(y);
        if(x == y) return (false);
        if(data[x] > data[y]) swap(x, y);
        data[x] += data[y];
        data[y] = x;
        return (true);
    }

    int find(int k){
        if(data[k] < 0) return (k);
        return (data[k] = find(data[k]));
    }

    int size(int k){
        return (-data[find(k)]);
    }

    bool same(int x, int y){
        return (find(x) == find(y));
    }

    vector<vector<int> > groups(){
        vector<int> leader_buf(n), group_size(n);
        for(int i = 0; i < n; ++i){
            leader_buf[i] = find(i);
            group_size[leader_buf[i]]++;
        }
        vector<vector<int> > res(n);
        for(int i = 0; i < n; ++i){
            res[i].reserve(group_size[i]);
        }
        for(int i = 0; i < n; ++i){
            res[leader_buf[i]].emplace_back(i);
        }
        res.erase(
            remove_if(res.begin(), res.end(),
                      [&](const vector<int>& v){ return (v.empty()); }),
            res.end());
        return (res);
    }
};

signed main(){
    cin.tie(nullptr);
    ios_base::sync_with_stdio(false);
    cout.setf(ios_base::fixed);
    cout.precision(12);

    int h, w;
    cin >> h >> w;
    
    assert(1 <= h);
    assert(2 <= w);
    assert(h * w <= 500000);

    vector<vector<int> > a(h, vector<int>(w));

    cin >> a;
    
    for (auto v : a) {
        for (auto e : v) {
            assert(1 <= e && e <= 1000000000);
        }
    }

    UnionFind uf(h * w);

    auto conv = [&](int r, int c) {
        return (r * w + c);
    };
    auto inside = [&](int r, int c) {
        return (0 <= r && r < h && 0 <= c && c < w);
    };

    vector<int> l(w), r(w);

    int cnt = 0;
    for (int j = 0; j < w; ++j) {
        cnt += h;
        for (int i = 0; i < h; ++i) {
            if (inside(i, j - 1) && a[i][j] == a[i][j - 1]) {
                cnt -= uf.unite(conv(i, j - 1), conv(i, j));
            }
        }
        for (int i = 0; i < h; ++i) {
            if (inside(i - 1, j) && a[i][j] == a[i - 1][j]) {
                cnt -= uf.unite(conv(i - 1, j), conv(i, j));
            }
        }
        l[j] = cnt;
    }

    uf = UnionFind(h * w);
    cnt = 0;

    for (int j = w - 1; j >= 0; --j) {
        cnt += h;
        for (int i = 0; i < h; ++i) {
            if (inside(i, j + 1) && a[i][j] == a[i][j + 1]) {
                cnt -= uf.unite(conv(i, j + 1), conv(i, j));
            }
        }
        for (int i = 0; i < h; ++i) {
            if (inside(i - 1, j) && a[i][j] == a[i - 1][j]) {
                cnt -= uf.unite(conv(i - 1, j), conv(i, j));
            }
        }
        r[j] = cnt;
    }

    int res = INF;

    for (int i = 0; i < w - 1; ++i) {
        chmin(res, l[i] + r[i + 1]);
    }

    // for (int i = 0; i < w; ++i) {
    //     cout << l[i] << ' ';
    // }
    // cout << endl;
    // for (int i = 0; i < w; ++i) {
    //     cout << r[i] << ' ';
    // }
    // cout << endl;

    cout << res << '\n';

    return (0);
}
accuracy: 1
time_ms: 50
memory_kb: 20632
final_rank: 2

code_snippet: #include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;
#define chmin(x,y) x = min(x,y)
#define chmax(x,y) x = max(x,y)
#define BIG_NUM 2000000000
#define HUGE_NUM 4000000000000000000 //オーバーフローに注意
#define MOD 1000000007
#define EPS 0.000000001




enum Type{
	NORMAL,
	REV,
};
Type type_array[2] = {NORMAL,REV};

#define SIZE 500005

int H,W;
int ***table;
int N;
int boss[2][SIZE];


int get_boss(Type type,int id){
	if(boss[type][id] == id)return id;
	else{
		return boss[type][id] = get_boss(type,boss[type][id]);
	}
}

int isSame(Type type,int x,int y){
	return get_boss(type,x) == get_boss(type,y);
}

void unite(Type type,int x,int y){
	int boss_x = get_boss(type,x);
	int boss_y = get_boss(type,y);

	if(boss_x == boss_y)return;

	//より番号が小さい方をボスとする
	if(boss_x < boss_y){


		boss[type][boss_y] = boss_x;

	}else{

		boss[type][boss_x] = boss_y;
	}
}

int getIndex(int row,int col){

	return row+H*col;
}

int main(){

	scanf("%d %d",&H,&W);
	N = H*W;

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

			boss[i][k] = k;
		}
	}

	table = new int**[2];
	for(int i = 0; i < 2; i++){

		table[i] = new int*[H];
		for(int row = 0; row < H; row++){

			table[i][row] = new int[W];
		}
	}


	for(int row = 0; row < H; row++){
		for(int col = 0; col < W; col++){

			scanf("%d",&table[NORMAL][row][col]);
		}
	}

	for(int col = 0; col < W; col++){
		for(int row = 0; row < H; row++){

			table[REV][row][(W-1)-col] = table[NORMAL][row][col]; //左右反転した表を作る
		}
	}

	//あらかじめ縦方向にuniteしておく
	for(int i = 0; i < 2; i++){
		for(int col = 0; col < W; col++){
			for(int row = 0; row < H-1; row++){

				int a = getIndex(row,col);
				int b = getIndex(row+1,col);

				if(table[i][row][col] == table[i][row+1][col]){

					unite(type_array[i],a,b);
				}
			}
		}
	}


	vector<int> V[2];

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

		//左端の列のグループ数を数える
		set<int> SET;
		for(int row = 0; row < H; row++){

			SET.insert(get_boss(type_array[i],getIndex(row,0)));
		}

		V[type_array[i]].push_back(SET.size());

		//境界をuniteする
		for(int col = 1; col <= W-1; col++){
			for(int row = 0; row < H; row++){
				if(table[i][row][col-1] == table[i][row][col]){

					int from = getIndex(row,col-1);
					int to = getIndex(row,col);

					int a = get_boss(type_array[i],from);
					int b = get_boss(type_array[i],to);

					SET.erase(a);
					SET.erase(b);

					unite(type_array[i],from,to);

					int c = get_boss(type_array[i],from);
					SET.insert(c);
				}
			}
			//uniteされなかった行のボスを集める
			for(int row = 0; row < H; row++){

				int from = getIndex(row,col);
				int a = get_boss(type_array[i],from);

				SET.insert(a);
			}
			V[type_array[i]].push_back(SET.size());
		}
	}

	reverse(V[REV].begin(),V[REV].end());

	int ans = BIG_NUM;
	for(int col = 0; col < W-1; col++){

		chmin(ans,V[NORMAL][col]+V[REV][col+1]);
	}

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

	return 0;
}
accuracy: 1
time_ms: 520
memory_kb: 50120
final_rank: 13

code_snippet: #include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;
#define chmin(x,y) x = min(x,y)
#define chmax(x,y) x = max(x,y)
#define BIG_NUM 2000000000
#define HUGE_NUM 4000000000000000000 //オーバーフローに注意
#define MOD 1000000007
#define EPS 0.000000001




enum Type{
	NORMAL,
	REV,
};
Type type_array[2] = {NORMAL,REV};

#define SIZE 500005

int H,W;
int ***table;
int N;
int boss[2][SIZE];


int get_boss(Type type,int id){
	if(boss[type][id] == id)return id;
	else{
		return boss[type][id] = get_boss(type,boss[type][id]);
	}
}

int isSame(Type type,int x,int y){
	return get_boss(type,x) == get_boss(type,y);
}

void unite(Type type,int x,int y){
	int boss_x = get_boss(type,x);
	int boss_y = get_boss(type,y);

	if(boss_x == boss_y)return;

	//より番号が小さい方をボスとする
	if(boss_x < boss_y){


		boss[type][boss_y] = boss_x;

	}else{

		boss[type][boss_x] = boss_y;
	}
}

int getIndex(int row,int col){

	return row+H*col;
}

int main(){

	scanf("%d %d",&H,&W);
	N = H*W;

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

			boss[i][k] = k;
		}
	}

	table = new int**[2];
	for(int i = 0; i < 2; i++){

		table[i] = new int*[H];
		for(int row = 0; row < H; row++){

			table[i][row] = new int[W];
		}
	}


	for(int row = 0; row < H; row++){
		for(int col = 0; col < W; col++){

			scanf("%d",&table[NORMAL][row][col]);
		}
	}

	for(int col = 0; col < W; col++){
		for(int row = 0; row < H; row++){

			table[REV][row][(W-1)-col] = table[NORMAL][row][col]; //左右反転した表を作る
		}
	}

	//あらかじめ縦方向にuniteしておく
	for(int i = 0; i < 2; i++){
		for(int col = 0; col < W; col++){
			for(int row = 0; row < H-1; row++){

				int a = getIndex(row,col);
				int b = getIndex(row+1,col);

				if(table[i][row][col] == table[i][row+1][col]){

					unite(type_array[i],a,b);
				}
			}
		}
	}


	vector<int> V[2];

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

		//printf("\n\ni:%d\n",i);

		//左端の列のグループ数を数える
		set<int> SET;
		for(int row = 0; row < H; row++){

			SET.insert(get_boss(type_array[i],getIndex(row,0)));
		}

		V[type_array[i]].push_back(SET.size());

		//printf("左端のグループ数:%lld\n",SET.size());

		//境界をuniteする
		for(int col = 1; col <= W-1; col++){
			for(int row = 0; row < H; row++){
				if(table[i][row][col-1] == table[i][row][col]){

					int from = getIndex(row,col-1);
					int to = getIndex(row,col);

					int a = get_boss(type_array[i],from);
					int b = get_boss(type_array[i],to);

					SET.erase(a);
					SET.erase(b);

					unite(type_array[i],from,to);

					int c = get_boss(type_array[i],from);
					SET.insert(c);
				}
			}
			//uniteされなかった行のボスを集める
			for(int row = 0; row < H; row++){

				int from = getIndex(row,col);
				int a = get_boss(type_array[i],from);

				SET.insert(a);
			}
			//printf("col:%d グループ数:%lld\n",col,SET.size());
			V[type_array[i]].push_back(SET.size());
		}
	}

	reverse(V[REV].begin(),V[REV].end());

	int ans = BIG_NUM;
	for(int col = 0; col < W-1; col++){

		chmin(ans,V[NORMAL][col]+V[REV][col+1]);
	}

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

	return 0;
}
accuracy: 1
time_ms: 510
memory_kb: 50032
final_rank: 12

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

struct dsu {
    public:
    int csz;
    dsu() : _n(0) {}
    dsu(int n) : _n(n), csz(n), parent_or_size(n, -1) {}

    int merge(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        int x = leader(a), y = leader(b);
        if (x == y) return x;
        if (-parent_or_size[x] < -parent_or_size[y]) std::swap(x, y);
        csz--;
        parent_or_size[x] += parent_or_size[y];
        parent_or_size[y] = x;
        return x;
    }

    bool same(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        return leader(a) == leader(b);
    }

    int leader(int a) {
        assert(0 <= a && a < _n);
        if (parent_or_size[a] < 0) return a;
        return parent_or_size[a] = leader(parent_or_size[a]);
    }

    int size(int a) {
        assert(0 <= a && a < _n);
        return -parent_or_size[leader(a)];
    }

    std::vector<std::vector<int>> groups() {
        std::vector<int> leader_buf(_n), group_size(_n);
        for (int i = 0; i < _n; i++) {
            leader_buf[i] = leader(i);
            group_size[leader_buf[i]]++;
        }
        std::vector<std::vector<int>> result(_n);
        for (int i = 0; i < _n; i++) {
            result[i].reserve(group_size[i]);
        }
        for (int i = 0; i < _n; i++) {
            result[leader_buf[i]].push_back(i);
        }
        result.erase(
            std::remove_if(result.begin(), result.end(),
                           [&](const std::vector<int>& v) { return v.empty(); }),
            result.end());
        return result;
    }

    private:
    int _n;
    // root node: -1 * component size
    // otherwise: parent
    std::vector<int> parent_or_size;
};

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int h, w;
    cin >> h >> w;
    vector<vector<int>> A(h, vector<int>(w));
    for(int y = 0; y < h; y++){
        for(int x = 0; x < w; x++){
            cin >> A[y][x];
        }
    }
    dsu uf(h * w);
    auto f = [&](int y, int x){return y * w + x; };
    vector<int> L(w);
    for(int x = 0; x + 1 < w; x++){
        if(x){
            for(int y = 0; y < h; y++){
                if(A[y][x] == A[y][x - 1])uf.merge(f(y, x), f(y, x - 1));
            }
        }
        for(int y = 1; y < h; y++){
            if(A[y][x] == A[y - 1][x])uf.merge(f(y, x), f(y - 1, x));
        }
        L[x] = uf.csz - h * (w - 1 - x);
    }
    uf = dsu(h * w);
    int ans = h * w;
    for(int x = w - 1; x >= 1; x--){
        if(x != w - 1){
            for(int y = 0; y < h; y++){
                if(A[y][x] == A[y][x + 1])uf.merge(f(y, x), f(y, x + 1));
            }
        }
        for(int y = 1; y < h; y++){
            if(A[y][x] == A[y - 1][x])uf.merge(f(y, x), f(y - 1, x));
        }
        ans = min(ans, L[x - 1] + uf.csz - h * x);
    }
    cout << ans << '\n';
}
accuracy: 1
time_ms: 50
memory_kb: 20668
final_rank: 3