Problem:
Score : 500 points

Problem Statement

We have a two-dimensional grid with H \times W squares. There are M targets to destroy in this grid - the position of the i-th target is \left(h_i, w_i \right).

Takahashi will choose one square in this grid, place a bomb there, and ignite it. The bomb will destroy all targets that are in the row or the column where the bomb is placed. It is possible to place the bomb at a square with a target.

Takahashi is trying to maximize the number of targets to destroy. Find the maximum number of targets that can be destroyed.

Constraints

All values in input are integers.

1 \leq H, W \leq 3 \times 10^5

1 \leq M \leq \min\left(H\times W, 3 \times 10^5\right)

1 \leq h_i \leq H

1 \leq w_i \leq W

\left(h_i, w_i\right) \neq \left(h_j, w_j\right) \left(i \neq j\right)

Input

Input is given from Standard Input in the following format:

H W M
h_1 w_1
\vdots
h_M w_M

Output

Print the answer.

Sample Input 1

2 3 3
2 2
1 1
1 3

Sample Output 1

3

We can destroy all the targets by placing the bomb at \left(1, 2\right).

Sample Input 2

3 3 4
3 3
3 1
1 1
1 2

Sample Output 2

3

Sample Input 3

5 5 10
2 5
4 3
2 3
5 5
2 2
5 4
5 3
5 1
3 5
1 4

Sample Output 3

6

Solution in C++ (GCC 9.2.1) (judge verdict: Runtime Error)

#include <iostream>
#include <string>
#include <cstring>
#include <stack>
#include <queue>
#include <cctype>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <cmath>
#include <stdlib.h>
#include <tuple>
#include <iomanip>
#include <iterator>
#include <stdio.h>
#include <fstream>
#include <time.h>
#include <bit>
#include <bitset>
#include <cstdint>
using namespace std;
const double PI = 3.14159265358979323846;
const long long int MOD = 1000000000 + 7;
using ll = long long;

struct UnionFind {
    vector<int> parent;

    UnionFind(int N) : parent(N) {
        for (int i = 0; i < N; i++) {
            parent[i] = -1;
        }
    }

    int root(int i) {
        if (parent[i] < 0) {
            return i;
        }
        return (parent[i] = root(parent[i]));
    }

    bool unite(int from, int to) {
        int rx = root(from);
        int ry = root(to);
        if (rx != ry) {
            parent[ry] += parent[rx];
            parent[rx] = ry;

            return true;
        }
        else {
            return false;
        }
    }

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

    int treeSize(int x) {


        return -parent[root(x)];
    }
};

long long int modpow(long long int base, long long int pow, long long int mod) {
    if (pow == 1) {
        return base;
    }
    else if (pow == 0) {
        return 1;
    }

    if (pow % 2 == 0) {
        auto temp = modpow(base, pow / 2, mod);
        return (temp * temp) % mod;
    }
    else {
        return (base * modpow(base, pow - 1, mod)) % mod;
    }
}

long long int moddiv(long long int X, long long int Y, long long int mod) {
    auto fermatDiv = modpow(Y, mod - 2, mod);

    return (X * fermatDiv) % mod;
}

long long modCombination(long long left, long long right, long long int mod) {
    long long answer = 1;
    if (left > right) {
        for (long long i = 0; i < right; i++) {
            answer = (answer * (left - i)) % mod;
            answer = moddiv(answer, (i + 1), mod);
        }
    }
    return answer;
}

bool IsPrime(long long N) {
    if (N == 1) {
        return false;
    }
    for (long long i = 2; i * i <= N; i++) {
        if (N % i == 0) {
            return false;
        }
    }


    return true;
}


vector<pair<long long, long long> > prime_factorize(long long N) {
    vector<pair<long long, long long> > res;
    for (long long a = 2; a * a <= N; ++a) {
        if (N % a != 0) continue;
        long long ex = 0; // 指数

        // 割れる限り割り続ける
        while (N % a == 0) {
            ++ex;
            N /= a;
            
        }

        // その結果を push
        res.push_back({ a, ex });
    }

    // 最後に残った数について
    if (N != 1) res.push_back({ N, 1 });
    return res;
}

vector<long long> enum_divisors(long long N) {
    vector<long long> res;
    for (long long i = 1; i * i <= N; ++i) {
        if (N % i == 0) {
            res.push_back(i);
            // 重複しないならば i の相方である N/i も push
            if (N / i != i) res.push_back(N / i);
        }
    }
    // 小さい順に並び替える
    sort(res.begin(), res.end());
    return res;
}

long long gcd(long long a, long long b) {
    if (b > a) {
        long long temp = b;
        b = a;
        a = temp;
    }
    //cout << "a:" << a << "b:" << b << endl;
    long long c = a % b;
    if (c == 0) {
        return b;
    }
    else {
        return gcd(b, c);
    }
}



int main() {

    /*ll H, W;
    cin >> H >> W;
    ll Ch, Cw;
    cin >> Ch >> Cw;
    Ch--;
    Cw--;
    ll Dh, Dw;
    cin >> Dh >> Dw;
    Dh--;
    Dw--;

    vector<vector<char>> s(H, vector<char>(W));
    UnionFind unionFind(H * W);
    for (ll i = 0; i < H; i++) {
        for (ll j = 0; j < W; j++) {
            char c;
            cin >> c;

            s[i][j] = c;
            if (j > 0) {
                if (s[i][j - 1] == '.') {
                    unionFind.unite(i * W + j, i * W + j - 1);
                }
            }
            if (i > 0) {
                if (s[i - 1][j] == '.') {
                    unionFind.unite(i * W + j, (i - 1) * W + j);
                }
            }
        }
    }

    if (unionFind.same(Ch * W + Cw, Dh * W + Dw)) {
        cout << 0 << endl;
        return 0;
    }


    vector<int> unionNodeTable;

    for (ll i = 0; i < H; i++) {
        for (ll j = 0; j < W; j++) {
            
            if (s[i][j] == '.' &&
                find(unionNodeTable.begin(), unionNodeTable.end(), unionFind.root(i * W + j)) == unionNodeTable.end()) {
                unionNodeTable.push_back(unionFind.root(i * W + j));
            }
        }
    }


    vector<vector<bool>> graph(unionNodeTable.size(), vector<bool>(unionNodeTable.size()));

    for (ll i = 0; i < H; i++) {
        for (ll j = 0; j < W; j++) {

            if (s[i][j] == '.') {
                for (ll di = -2; di <= 2; di++) {
                    for (ll dj = -2; dj <= 2; dj++) {
                        ll y = i + di, x = j + dj;

                        if (y < 0 || y >= H || x < 0 || x >= W) {
                            continue;
                        }

                        if (s[y][x] == '.') {
                            int root1 = unionFind.root(i * W + j);
                            int root2 = unionFind.root(y * W + x);
                            
                            auto itr1 = find(unionNodeTable.begin(), unionNodeTable.end(), root1);
                            int index1 = distance(unionNodeTable.begin(), itr1);
                            
                            auto itr2 = find(unionNodeTable.begin(), unionNodeTable.end(), root2);
                            int index2 = distance(unionNodeTable.begin(), itr2);

                            graph[index1][index2] = true;
                            graph[index2][index1] = true;
                        }
                    }
                }
            }
        }
    }


    vector<int> dist(unionNodeTable.size(), -1);
    queue<int> que;

    int startRoot = unionFind.root(Ch * W + Cw);
    auto startItr = find(unionNodeTable.begin(), unionNodeTable.end(), startRoot);
    int startIndex = distance(unionNodeTable.begin(), startItr);
    que.push(startIndex);
    dist[startIndex] = 0;

    while (!que.empty()) {
        int v = que.front();
        que.pop();

        for (int i = 0; i < unionNodeTable.size(); i++) {
            if (graph[v][i]) {
                if (dist[i] != -1) {
                    continue;
                }

                dist[i] = dist[v] + 1;
                que.push(i);
            }
        }
    }

    int goalRoot = unionFind.root(Dh * W + Dw);
    auto goalItr = find(unionNodeTable.begin(), unionNodeTable.end(), goalRoot);
    int goalIndex = distance(unionNodeTable.begin(), goalItr);

    cout << dist[goalIndex] << endl;*/



    ll H, W, M;
    cin >> H >> W >> M;

    vector<ll> hCounts(H + 1, 0);
    vector<ll> wCounts(W + 1, 0);

    vector<vector<bool>> field(H + 1, vector<bool>(W + 1, false));
    vector<pair<ll, ll>> items;
    for (ll i = 0; i < M; i++) {
        ll h, w;
        cin >> h >> w;
        hCounts[h]++;
        wCounts[w]++;

        field[h][w] = true;

        items.emplace_back(h, w);
    }

    ll hMaxIndex = 0;
    ll hMax = 0;
    vector<ll> hMaxIndices;
    for (ll i = 0; i <= H; i++) {
        if (hCounts[i] > hMax) {
            hMaxIndex = i;
            hMax = hCounts[i];
            hMaxIndices = vector<ll>();
            hMaxIndices.push_back(i);
        }
        else if (hCounts[i] == hMax) {
            hMaxIndices.push_back(i);
        }
    }

    ll wMaxIndex = 0;
    ll wMax = 0;
    vector<ll> wMaxIndices;
    for (ll i = 0; i <= W; i++) {
        if (wCounts[i] > wMax) {
            wMaxIndex = i;
            wMax = wCounts[i];
            wMaxIndices = vector<ll>();
            wMaxIndices.push_back(i);
        }
        else if (wCounts[i] == wMax) {
            wMaxIndices.push_back(i);
        }
    }

    ll crossItemsCount = 0;
    for (auto p : items) {
        auto hItr = binary_search(hMaxIndices.begin(), hMaxIndices.end(), p.first);
        auto wItr = binary_search(wMaxIndices.begin(), wMaxIndices.end(), p.second);
        if (hItr && wItr) {
            crossItemsCount++;
        }
    }

    ll crossCount = hMaxIndices.size();
    crossCount *= wMaxIndices.size();
    if (crossItemsCount == crossCount) {
        cout << hMax + wMax - 1 << endl;
    }
    else {
        cout << hMax + wMax << endl;
    }

    return 0;
}