Problem:
Problem I:
ThreeRooks
ねこがチェスの練習をしている.
ねこは, $X \times Y$ のチェス盤の上にルークを 3 つ置こうとしている. このチェス盤の $K$ 個のマス目にはうさぎが座っている. $i$ 匹目のうさぎの座標は $(x[i], y[i])$ である. ただし, チェス盤の左上端のマス目の座標を $(0, 0)$, 右下端のマス目の座標を $(X-1, Y-1)$ とする。うさぎが座っている場所にはルークを置くことができない. また, 1 つのマス目に複数個のルークを置くことはできない.
どの 2 つのルークも互いに攻撃し合わないようにルークを3 つ置く方法は何通りあるか, mod 1,000,000,007 で求めよ. 2 つのルークは同じ行または同じ列にあり, 間にうさぎが座っていない場合に互いに攻撃しあうものとする.
Constraints
$X$, $Y$ will be between 1 and 1,000,000,000, inclusive.
$K$ will be between 1 and 100,000, inclusive.
$x_i$ will be between 0 and $X-1$, inclusive.
$y_i$ will be between 0 and $Y-1$, inclusive.
No two rabbits sit on the same cell.
Input
入力は以下の形式で与えられる:
$X$ $Y$ $K$
$x_1$ $y_1$
...
$x_K$ $y_K$
Output
ルークの配置の個数を 1,000,000,007 で割ったあまりを表す整数を 1 行に出力せよ.
Sample Input 1
3 3 1
0 0
Sample Output 1
4
Sample Input 2
5 8 2
2 2
4 5
Sample Output 2
3424

Submitted solutions:

code_snippet: // AOJ #2377 ThreeRooks
// 2025.4.6

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MOD = 1000000007;

#define gc() getchar_unlocked()
#define pc(c) putchar_unlocked(c)

int Cin() {
	int n = 0; int c = gc();
	do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
	return n;
}

inline ll modAdd(ll a, ll b){ return (a+b) % MOD; }
inline ll modSub(ll a, ll b){ return (a-b) % MOD; }
inline ll modMul(ll a, ll b){ return a * b % MOD; }

ll prod(ll a, int c) {
    ll res = 1;
//  a %= MOD;
    for (int i = 0; i < c; i++){
        res = modMul(res, a);
        a = modSub(a, 1);
    }
    return res;
}

struct BIT {
    int n;
    vector<ll> tree;
    BIT(int n): n(n), tree(n+1, 0) { }
    void update(int idx, ll delta) {
        for(; idx <= n; idx += idx & -idx) tree[idx] = modAdd(tree[idx], delta);
    }
    ll query(int idx) {
        ll res = 0;
        for(; idx; idx -= idx & -idx) res = modAdd(res, tree[idx]);
        return res;
    }
    ll query(int l, int r) {
        if(l > r) return 0;
        return modSub(query(r), query(l-1));
    }
    ll point(int idx) {
        return query(idx, idx);
    }
    void set(int idx, ll val) {
        ll cur = point(idx);
        ll delta = modSub(val, cur);
        update(idx, delta);
    }
};

int main(){
    int X = Cin(), Y = Cin(), K = Cin();
    vector<ll> rx(K), ry(K);
    for (int i = 0; i < K; i++) rx[i] = Cin(), ry[i] = Cin();
    ll totAllowed = modSub(modMul(X, Y), K);

    set<ll> setR, setC;
    for (int i = 0; i < K; i++){
        setR.insert(rx[i]);
        setC.insert(ry[i]);
    }
    vector<ll> vx(setR.begin(), setR.end()), vy(setC.begin(), setC.end());
    int rn = vx.size(), cn = vy.size();
    unordered_map<ll,int> compR, compC;
    for (int i = 0; i < rn; i++) compR[vx[i]] = i;
    for (int j = 0; j < cn; j++) compC[vy[j]] = j;

    vector<vector<int>> rowsWith(rn);
    vector<vector<int>> colsWith(cn);
    for (int i = 0; i < K; i++){
        int r = compR[rx[i]];
        int c = compC[ry[i]];
        rowsWith[r].push_back(c);
        colsWith[c].push_back(r);
    }
    for (int i = 0; i < rn; i++) sort(rowsWith[i].begin(), rowsWith[i].end());
    for (int j = 0; j < cn; j++) sort(colsWith[j].begin(), colsWith[j].end());

    ll freeRows = modSub(X, rn);
    ll freeCols = modSub(Y, cn);

    ll ans2 = 0, ans3 = 0, ans4 = 0;
    ans2 = modAdd(modMul(freeRows, prod(Y, 2)), modMul(freeCols, prod(X, 2)));
    ans3 = modAdd(modMul(freeRows, prod(Y, 3)), modMul(freeCols, prod(X, 3)));
    ans4 = modMul(modMul(freeRows, freeCols), modMul(X-1, Y-1));

    vector<ll> p(rn, 0);
    for (int r = 0; r < rn; r++) if (!rowsWith[r].empty()) p[r] = vy[ rowsWith[r][0] ];

    BIT bit1(rn), bit2(rn);
    for (int r = 0; r < rn; r++){
        ll val = modSub(p[r], 1);
        bit1.update(r+1, val);
        bit2.update(r+1, prod(p[r]-1, 2));
    }

    vector<int> ptr(rn, 0);
    for (int i = 0; i <= cn; i++){
        ll gap = 0;
        if(i == 0) gap = vy[0];
        else if(i == cn) gap = Y - 1 - vy[cn-1];
        else gap = vy[i] - 1 - vy[i-1];
        ans2 = modAdd(ans2, modMul(bit1.query(1, rn), gap));
        ans3 = modAdd(ans3, modMul(bit2.query(1, rn), gap));
        ans4 = modAdd(ans4, modMul(modMul(bit1.query(1, rn), gap), (X-1)));
        if(i == cn) break;

        vector<int> &curRows = colsWith[i];
        int L = 1;
        for (int j = 0; j <= (int)curRows.size(); j++){
            int r_idx = (j < (int)curRows.size() ? curRows[j] + 1 : rn+1);
            int segLen = 0;
            if(j == 0)             segLen = (r_idx <= rn ? vx[r_idx-1] : X);
            else if(r_idx == rn+1) segLen = X - 1 - vx[curRows[j-1]];
            else                   segLen = vx[r_idx-1] - vx[curRows[j-1]] - 1;

            ans2 = modAdd(ans2, bit1.query(L, r_idx-1));
            ans3 = modAdd(ans3, bit2.query(L, r_idx-1));
            ans2 = modAdd(ans2, prod(segLen, 2));
            ans3 = modAdd(ans3, prod(segLen, 3));
            ll S = modAdd(modMul(Y-1, segLen - ((r_idx-1) - (L-1))), bit1.query(L, r_idx-1));
            ans4 = modAdd(ans4, modMul(segLen - 1, S));

            if(r_idx <= rn){
                ptr[r_idx-1]++;
                ll tmp;
                if(ptr[r_idx-1] == (int)rowsWith[r_idx-1].size())
                    tmp = Y - 1 - vy[ rowsWith[r_idx-1].back() ];
                else
                    tmp = vy[ rowsWith[r_idx-1][ptr[r_idx-1]] ] - 1 - vy[ rowsWith[r_idx-1][ptr[r_idx-1]-1] ];
                ll newVal = modSub(tmp, 1);
                bit1.set(r_idx, newVal);
                ll newVal2 = prod(tmp-1, 2);
                bit2.set(r_idx, newVal2);
            }
            L = r_idx + 1;
        }
    }

    ll totPerm = prod(totAllowed, 3);
    ans2 = modMul(ans2, totAllowed - 2);
    ll ans = totPerm;
    ans = modSub(ans, modMul(3, ans2));
    ans = modAdd(ans, modMul(2, ans3));
    ans = modAdd(ans, modMul(6, ans4));
    ans = modMul(ans, 166666668);
    cout << (ans+MOD) % MOD << endl;
    return 0;
}
accuracy: 1
time_ms: 130
memory_kb: 37100
final_rank: 3

code_snippet: // AOJ #2377 ThreeRooks
// 2025.4.6

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MOD = 1000000007;

#define gc() getchar_unlocked()
#define pc(c) putchar_unlocked(c)

int Cin() {
	int n = 0; int c = gc();
	do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
	return n;
}

inline ll modAdd(ll a, ll b){ return (a+b) % MOD; }
inline ll modSub(ll a, ll b){ return ((a-b) % MOD + MOD) % MOD; }
inline ll modMul(ll a, ll b){ return ((a % MOD) * (b % MOD)) % MOD; }

ll prod(ll a, int c) {
    ll res = 1;
    a %= MOD;
    for (int i = 0; i < c; i++){
        res = modMul(res, a);
        a = modSub(a, 1);
    }
    return res;
}

struct BIT {
    int n;
    vector<ll> tree;
    BIT(int n): n(n), tree(n+1, 0) { }
    void update(int idx, ll delta) {
        for(; idx <= n; idx += idx & -idx) tree[idx] = modAdd(tree[idx], delta);
    }
    ll query(int idx) {
        ll res = 0;
        for(; idx; idx -= idx & -idx) res = modAdd(res, tree[idx]);
        return res;
    }
    ll query(int l, int r) {
        if(l > r) return 0;
        return modSub(query(r), query(l-1));
    }
    ll point(int idx) {
        return query(idx, idx);
    }
    void set(int idx, ll val) {
        ll cur = point(idx);
        ll delta = modSub(val, cur);
        update(idx, delta);
    }
};

int main(){
    int X = Cin(), Y = Cin(), K = Cin();
    vector<ll> rx(K), ry(K);
    for (int i = 0; i < K; i++) rx[i] = Cin(), ry[i] = Cin();
    ll totAllowed = modSub(modMul(X, Y), K);

    set<ll> setR, setC;
    for (int i = 0; i < K; i++){
        setR.insert(rx[i]);
        setC.insert(ry[i]);
    }
    vector<ll> vx(setR.begin(), setR.end()), vy(setC.begin(), setC.end());
    int rn = vx.size(), cn = vy.size();
    unordered_map<ll,int> compR, compC;
    for (int i = 0; i < rn; i++) compR[vx[i]] = i;
    for (int j = 0; j < cn; j++) compC[vy[j]] = j;

    vector<vector<int>> rowsWith(rn);
    vector<vector<int>> colsWith(cn);
    for (int i = 0; i < K; i++){
        int r = compR[rx[i]];
        int c = compC[ry[i]];
        rowsWith[r].push_back(c);
        colsWith[c].push_back(r);
    }
    for (int i = 0; i < rn; i++) sort(rowsWith[i].begin(), rowsWith[i].end());
    for (int j = 0; j < cn; j++) sort(colsWith[j].begin(), colsWith[j].end());

    ll freeRows = modSub(X, rn);
    ll freeCols = modSub(Y, cn);

    ll ans2 = 0, ans3 = 0, ans4 = 0;
    ans2 = modAdd(modMul(freeRows, prod(Y, 2)), modMul(freeCols, prod(X, 2)));
    ans3 = modAdd(modMul(freeRows, prod(Y, 3)), modMul(freeCols, prod(X, 3)));
    ans4 = modMul(modMul(freeRows, freeCols), modMul(X-1, Y-1));

    vector<ll> p(rn, 0);
    for (int r = 0; r < rn; r++) if (!rowsWith[r].empty()) p[r] = vy[ rowsWith[r][0] ];

    BIT bit1(rn), bit2(rn);
    for (int r = 0; r < rn; r++){
        ll val = modSub(p[r], 1);
        bit1.update(r+1, val);
        bit2.update(r+1, prod(p[r]-1, 2));
    }

    vector<int> ptr(rn, 0);
    for (int i = 0; i <= cn; i++){
        ll gap = 0;
        if(i == 0) gap = vy[0];
        else if(i == cn) gap = Y - 1 - vy[cn-1];
        else gap = vy[i] - 1 - vy[i-1];
        ans2 = modAdd(ans2, modMul(bit1.query(1, rn), gap));
        ans3 = modAdd(ans3, modMul(bit2.query(1, rn), gap));
        ans4 = modAdd(ans4, modMul(modMul(bit1.query(1, rn), gap), (X-1)));
        if(i == cn) break;

        vector<int> &curRows = colsWith[i];
        int L = 1;
        for (int j = 0; j <= (int)curRows.size(); j++){
            int r_idx = (j < (int)curRows.size() ? curRows[j] + 1 : rn+1);
            int segLen = 0;
            if(j == 0)             segLen = (r_idx <= rn ? vx[r_idx-1] : X);
            else if(r_idx == rn+1) segLen = X - 1 - vx[curRows[j-1]];
            else                   segLen = vx[r_idx-1] - vx[curRows[j-1]] - 1;

            ans2 = modAdd(ans2, bit1.query(L, r_idx-1));
            ans3 = modAdd(ans3, bit2.query(L, r_idx-1));
            ans2 = modAdd(ans2, prod(segLen, 2));
            ans3 = modAdd(ans3, prod(segLen, 3));
            ll S = modAdd(modMul(Y-1, segLen - ((r_idx-1) - (L-1))), bit1.query(L, r_idx-1));
            ans4 = modAdd(ans4, modMul(segLen - 1, S));

            if(r_idx <= rn){
                ptr[r_idx-1]++;
                ll tmp;
                if(ptr[r_idx-1] == (int)rowsWith[r_idx-1].size())
                    tmp = Y - 1 - vy[ rowsWith[r_idx-1].back() ];
                else
                    tmp = vy[ rowsWith[r_idx-1][ptr[r_idx-1]] ] - 1 - vy[ rowsWith[r_idx-1][ptr[r_idx-1]-1] ];
                ll newVal = modSub(tmp, 1);
                bit1.set(r_idx, newVal);
                ll newVal2 = prod(tmp-1, 2);
                bit2.set(r_idx, newVal2);
            }
            L = r_idx + 1;
        }
    }

    ll totPerm = prod(totAllowed, 3);
    ans2 = modMul(ans2, totAllowed - 2);
    ll ans = totPerm;
    ans = modSub(ans, modMul(3, ans2));
    ans = modAdd(ans, modMul(2, ans3));
    ans = modAdd(ans, modMul(6, ans4));
    ans = modMul(ans, 166666668);
    cout << (ans+MOD) % MOD << endl;
    return 0;
}
accuracy: 1
time_ms: 140
memory_kb: 37152
final_rank: 4

code_snippet: // AOJ #2377 ThreeRooks
// 2025.4.6

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MOD = 1000000007;

#define gc() getchar_unlocked()
#define pc(c) putchar_unlocked(c)

int Cin() {
	int n = 0; int c = gc();
	do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
	return n;
}

inline ll modAdd(ll a, ll b) { return (a + b) % MOD; }
inline ll modSub(ll a, ll b) { return ((a - b) % MOD + MOD) % MOD; }
inline ll modMul(ll a, ll b) { return ( (a % MOD) * (b % MOD) ) % MOD; }

ll prod(ll a, int c) {
    ll res = 1;
    a %= MOD;
    for (int i = 0; i < c; i++) {
        res = modMul(res, a);
        a = modSub(a, 1);
    }
    return res;
}

struct SegTree {
    int n;
    vector<ll> seg;
    SegTree(int sz) {
        n = 1;
        while(n < sz) n <<= 1;
        seg.assign(2*n, 0);
    }
    void update(int idx, ll val) {
        idx += n;
        seg[idx] = val % MOD;
        for(idx >>= 1; idx > 0; idx >>= 1)
            seg[idx] = modAdd(seg[2*idx], seg[2*idx+1]);
    }
    ll query(int l, int r) {
        ll res = 0;
        l += n; r += n;
        while(l < r) {
            if(l & 1) { res = modAdd(res, seg[l]); l++; }
            if(r & 1) { r--; res = modAdd(res, seg[r]); }
            l >>= 1, r >>= 1;
        }
        return res;
    }
    ll total() { return seg[1]; }
};

int main(){
    int X = Cin(), Y = Cin(), K = Cin();
    vector<ll> rx(K), ry(K);
    for (int i = 0; i < K; i++) rx[i] = Cin(), ry[i] = Cin();
    ll totAllowed = modSub(modMul(X, Y), K);

    set<ll> setR, setC;
    for (int i = 0; i < K; i++){
        setR.insert(rx[i]);
        setC.insert(ry[i]);
    }
    vector<ll> vx(setR.begin(), setR.end()), vy(setC.begin(), setC.end());
    int rn = vx.size(), cn = vy.size();
    unordered_map<ll,int> compR, compC;
    for (int i = 0; i < rn; i++) compR[vx[i]] = i;
    for (int j = 0; j < cn; j++) compC[vy[j]] = j;

    vector<vector<int>> rowsWith(rn);
    vector<vector<int>> colsWith(cn);
    for (int i = 0; i < K; i++){
        int cr = compR[rx[i]];
        int cc = compC[ry[i]];
        rowsWith[cr].push_back(cc);
        colsWith[cc].push_back(cr);
    }
    for (int i = 0; i < rn; i++) sort(rowsWith[i].begin(), rowsWith[i].end());
    for (int j = 0; j < cn; j++) sort(colsWith[j].begin(), colsWith[j].end());

    ll freeRows = modSub(X, rn);
    ll freeCols = modSub(Y, cn);

    ll ans2 = 0, ans3 = 0, ans4 = 0;
    ans2 = modAdd(ans2, modMul(freeRows, prod(Y, 2)));
    ans2 = modAdd(ans2, modMul(freeCols, prod(X, 2)));
    ans3 = modAdd(ans3, modMul(freeRows, prod(Y, 3)));
    ans3 = modAdd(ans3, modMul(freeCols, prod(X, 3)));
    ans4 = modAdd(ans4, modMul(modMul(freeRows, freeCols), modMul(X-1, Y-1)));

    vector<ll> p(rn, 0);
    for (int r = 0; r < rn; r++) if (!rowsWith[r].empty()) p[r] = vy[ rowsWith[r][0] ];

    SegTree seg1(rn), seg2(rn);
    for (int r = 0; r < rn; r++){
        ll val = (p[r] - 1) % MOD;
        seg1.update(r, val);
        seg2.update(r, prod(p[r]-1, 2));
    }

    vector<int> ptr(rn, 0);
    for (int i = 0; i <= cn; i++){
        ll gap = 0;
        if(i == 0) gap = vy[0];
        else if(i == cn) gap = (Y - 1 - vy[cn-1]);
        else gap = vy[i] - 1 - vy[i-1];
        ans2 = modAdd(ans2, modMul(seg1.total(), gap));
        ans3 = modAdd(ans3, modMul(seg2.total(), gap));
        ans4 = modAdd(ans4, modMul(modMul(seg1.total(), gap), (X - 1)));

        if(i == cn) break;
        vector<int> &curRows = colsWith[i];
        int L = 0;
        for (int j = 0; j <= (int)curRows.size(); j++){
            int ridx = (j < curRows.size() ? curRows[j] : rn);
            int segLen = 0;
            if(j == 0)          segLen = (ridx < rn ? vx[ridx] : X);
            else if(ridx == rn) segLen = X - 1 - vx[curRows[j-1]];
            else                segLen = vx[ridx] - vx[curRows[j-1]] - 1;
            ans2 = modAdd(ans2, seg1.query(L, ridx));
            ans3 = modAdd(ans3, seg2.query(L, ridx));
            ans2 = modAdd(ans2, prod(segLen, 2));
            ans3 = modAdd(ans3, prod(segLen, 3));
            ll S = modAdd(modMul(Y - 1, segLen - (ridx - L)), seg1.query(L, ridx));
            ans4 = modAdd(ans4, modMul(segLen - 1, S));

            if(ridx < rn) {
                ptr[ridx]++;
                ll tmp;
                if(ptr[ridx] == (int)rowsWith[ridx].size()) tmp = Y - 1 - vy[ rowsWith[ridx].back() ];
                else tmp = vy[ rowsWith[ridx][ptr[ridx]] ] - 1 - vy[ rowsWith[ridx][ptr[ridx]-1] ];
                seg1.update(ridx, tmp - 1);
                seg2.update(ridx, prod(tmp - 1, 2));
            }
            L = ridx + 1;
        }
    }

    ll totPerm = prod(totAllowed, 3);
    ans2 = modMul(ans2, (totAllowed - 2));
    ll res = totPerm;
    res = modSub(res, modMul(3, ans2));
    res = modAdd(res, modMul(2, ans3));
    res = modAdd(res, modMul(6, ans4));
    ll inv6 = 166666668;
    res = modMul(res, inv6);
    cout << (res + MOD) % MOD << "\n";
    return 0;
}
accuracy: 1
time_ms: 160
memory_kb: 39764
final_rank: 5

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

template <class T>
bool chmin(T &a, T b) {
	if(a > b) {
		a = b;
		return true;
	}
	return false;
}
template <class T>
bool chmax(T &a, T b) {
	if(a < b) {
		a = b;
		return true;
	}
	return false;
}
constexpr ll INF = 1e18;
constexpr ll e = -INF;

using vll = vector<long long>;

template <class T>
void view(const T &e) {
	cerr << e;
}
void view(const ll &e) {
	if(-e > INF / 2)
		cerr << "-INF";
	else if(e > INF / 2)
		cerr << "INF";
	else
		cerr << e;
}
template <class T>
void view(const vector<T> &v) {
	if(v.empty()) {
		cerr << "{ }";
		return;
	}
	cerr << "{ ";
	for(const auto &e : v) {
		view(e);
		cerr << ", ";
	}
	cerr << "\b\b }";
}
template <class T, class U>
void view(const map<T, U> &v) {
	cerr << "{\n";
	for(const auto &e : v) {
		cerr << '\t';
		view(e.first);
		cerr << ": ";
		view(e.second);
		cerr << ",\n";
	}
	cerr << "}";
}

void debug_out() {}
template <typename Head, typename... Tail>
void debug_out(Head H, Tail... T) {
	view(H);
	cerr << ", ";
	debug_out(T...);
}

#ifdef LOC
#define debug(...)                                           \
	do {                                                     \
		cerr << __LINE__ << " [" << #__VA_ARGS__ << "] : ["; \
		debug_out(__VA_ARGS__);                              \
		cerr << "\b\b]\n";                                   \
	} while(0)
#else
#define debug(...) void(0)
#endif

constexpr int mod = 1000000007;
struct mint {
	ll x;
	mint(ll y = 0) : x(y % mod) {
		if(x < 0) x += mod;
	}
	mint operator+(mint b) { return mint(x + b.x); }
	mint operator-(mint b) { return mint(x - b.x); }
	mint operator+=(mint b) { return *this = mint(x + b.x); }
	mint operator-=(mint b) { return *this = mint(x - b.x); }
	mint operator*(mint b) { return mint(x * b.x); }
	mint operator*=(mint b) { return *this = mint(x * b.x); }
	mint pow(ll n) {
		mint mult = x;
		mint ret = 1;
		while(n) {
			if(n & 1) ret *= mult;
			n >>= 1;
			mult *= mult;
		}
		return ret;
	}
	mint inv() { return pow(mod - 2); }

	mint operator/(mint b) { return mint(b.inv() * x); }
	mint operator/=(mint b) { return *this = *this / b; }
};

ostream &operator<<(ostream &os, const mint &x) {
	os << x.x;
	return os;
}

struct stsum {
	int n;
	vector<ll> dat;
	const ll e = 0LL;
	stsum(int len) : n(len), dat(len + len, e) {}
	int left(int par) { return par + par; }
	int right(int par) { return left(par) + 1; }
	void update(int idx, ll val) {
		idx += n;
		dat[idx] = val;
		idx >>= 1;
		while(idx) {
			dat[idx] = dat[left(idx)] + dat[right(idx)];
			idx >>= 1;
		}
	}
	ll sum(int lef, int rig) {
		lef += n;
		rig += n;
		ll ret = 0;
		while(lef < rig) {
			if(lef & 1) {
				ret += dat[lef];
				lef++;
			}
			if(rig & 1) {
				ret += dat[rig - 1];
				rig--;
			}
			lef >>= 1;
			rig >>= 1;
		}
		return ret;
	}
};

void view(stsum s) {
	vector<ll> vec;
	rep(i, s.n) { vec.push_back(s.sum(i, i + 1)); }
	view(vec);
}

struct cmp {
	vector<ll> xs;
	cmp(vector<ll> x) : xs(x) {
		sort(all(xs));
		xs.erase(unique(all(xs)), xs.end());
	}

	const int len() { return int(xs.size()); }

	int idx(ll val) {
		auto it = lower_bound(all(xs), val);
		if(it == xs.end()) return -1;
		return *it == val ? int(it - xs.begin()) : -1;
	}

	ll value(int idx) { return xs.at(idx); }
	ll operator[](int idx) { return xs.at(idx); }
};

tuple<mint, mint, mint> count_l23(ll h, ll w, vector<pair<ll, ll>> rocks) {
	map<ll, vll> rows, cols;
	vector<ll> xvec, yvec;
	for(auto [x, y] : rocks) {
		rows[x].push_back(y);
		cols[y].push_back(x);
		xvec.push_back(x);
		xvec.push_back(x + 1);
		yvec.push_back(y);
		yvec.push_back(y + 1);
	}
	xvec.push_back(0LL);
	xvec.push_back(h);
	yvec.push_back(0LL);
	yvec.push_back(w);
	cmp xs(xvec);
	cmp ys(yvec);

	debug(rows);
	debug(cols);

	foa(t, rows) sort(all(t.second));
	foa(t, cols) sort(all(t.second));

	int xmax = xs.len() - 1;
	int ymax = ys.len() - 1;

	auto ynxt = [&](ll col, ll xbegin) {
		if(!cols.count(col)) return h;
		auto &tate = cols[col];
		auto it = lower_bound(all(tate), xbegin);
		if(it == tate.end()) return h;
		return *it;
	};

	auto xnxt = [&](ll row, ll ybegin) {
		if(!rows.count(row)) return w;
		auto &yoko = rows[row];
		auto it = lower_bound(all(yoko), ybegin);
		if(it == yoko.end()) return w;
		return *it;
	};

	stsum st(ymax);
	rep(j, ymax) {
		ll lef = ys[j];
		ll rig = ys[j + 1];
		ll buttom = ynxt(lef, 0LL);
		st.update(j, buttom * (rig - lef));

		debug(buttom, lef, rig);
	}

	mint ansL = 0;
	mint ans2 = 0;
	mint ans3 = 0;
	rep(xid, xmax) {
		debug(xid);
		ll up = xs[xid];
		ll dn = xs[xid + 1];
		ll thick = dn - up;
		debug(xid, up, dn, thick);

		vll vec;
		if(rows.count(up)) vec = rows[up];
		debug(vec);
		debug(st);

		foa(mid, vec) {
			int j = ys.idx(mid);
			ll nx = ynxt(mid, dn + 1);
			st.update(j, nx);
		}

		vec.insert(vec.begin(), -1LL);
		vec.push_back(w);

		debug(vec);

		rep(j, int(vec.size()) - 1) {
			ll left = vec[j] + 1;
			ll right = vec[j + 1];
			ll rg = right - left;

			debug(left, right);

			if(rg == 0) continue;

			int lid = ys.idx(left);
			int rid = ys.idx(right);

			ll s = st.sum(lid, rid) - rg * up;
			debug(st);

			debug(lid, rid, s, st.sum(lid, rid));

			assert(s >= 0);
			ansL += mint(rg - 1) * thick * ((s - rg) + (s - thick * rg)) / 2;
			ans2 += mint(thick) * (rg) * (rg - 1) / 2;
			ans3 += mint(thick) * rg * (rg - 1) * (rg - 2) / 6;
		}

		debug(st);
	}

	return tuple(ansL, ans2, ans3);
}

mint solve(ll h) {
	ll w;
	cin >> w;
	int m;
	cin >> m;
	vector<pair<ll, ll>> rocks;
	rep(i, m) {
		ll x, y;
		cin >> x >> y;
		rocks.emplace_back(x, y);
	}

	ll spaces = h * w - m;
	assert(spaces > -1);

	mint triplets = mint(spaces) * mint(spaces - 1) * mint(spaces - 2) / 6;

	mint ls;
	mint two, three;
	tie(ls, two, three) = count_l23(h, w, rocks);
	for(auto &t : rocks) {
		t.first = h - 1 - t.first;
	}
	ls += get<0>(count_l23(h, w, rocks));

	for(auto &t : rocks) {
		swap(t.first, t.second);
	}
	swap(h, w);
	auto res = count_l23(h, w, rocks);
	two += get<1>(res);
	three += get<2>(res);

	debug(triplets, two, ls, three);

	return triplets - two * (spaces - 2) + ls + three * 2;
}

int main() {
	cout << fixed << setprecision(15);
	srand(unsigned(time(NULL)));
	ll n;
	while(cin >> n) {
		cout << solve(n) << '\n';
	};
}
accuracy: 1
time_ms: 1060
memory_kb: 40040
final_rank: 7

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i,a) for(int i=0;i<a;i++)
using ll=long long;
#define all(p) p.begin(),p.end()
const int mod=1e9+7;

struct seg_tree{
	vector<ll> seg;
	int _n;
	int s;
	seg_tree(int size){
		_n=1;
		while(_n<=size) _n*=2;
		s=size;
		seg.resize(_n*2);
	}
	void updata(int ind){
		seg[ind]=(seg[ind*2+1]+seg[ind*2])%mod;
		if(ind!=1) updata(ind/2);
	};
	void set(int ind,ll val){
		ind+=_n;
		seg[ind]=val;
		updata(ind/2);
	}
	ll query_s(int l,int r,int a,int b,int k){
		if(l<=a&&b<=r) return seg[k];
		if(r<=a||b<=l) return 0;
		ll tmp=0;
		tmp+=query_s(l,r,a,(a+b)/2,k*2);
		tmp+=query_s(l,r,(a+b)/2,b,k*2+1);
		return tmp;
	}
	ll query(int l,int r){
		assert(0<=l&&l<=r&&r<=s);
		return query_s(l,r,0,_n,1);
	}
	ll sum(){return seg[1];}
};

ll f(ll sta,ll c){
	ll tmp=1;
	sta%=mod;
	rep(i,c) tmp=(tmp*sta)%mod,sta--;
	return tmp;
}
int main(){
	ll X,Y,K;
	cin>>X>>Y>>K;
	ll em=(X*Y-K)%mod;
	set<ll> x_ind_set,y_ind_set;
	map<ll,int> x_ind_map,y_ind_map;
	vector<ll> x_ind_list,y_ind_list,p;
	ll ans2=0,ans3=0,ans4=0;
	vector<vector<int>> G,H;
	vector<int> G_ind;
	vector<ll> x(K),y(K);
	rep(i,K){
		cin>>x[i]>>y[i];
		x_ind_set.insert(x[i]);
		y_ind_set.insert(y[i]);
	}
	int x_ind=0,y_ind=0;
	for(auto v:x_ind_set) x_ind_map[v]=x_ind,x_ind++,x_ind_list.push_back(v);
	for(auto v:y_ind_set) y_ind_map[v]=y_ind,y_ind++,y_ind_list.push_back(v);
	G.resize(x_ind);
	G_ind.resize(x_ind,0);
	H.resize(y_ind);
	rep(i,K){
		G[x_ind_map[x[i]]].push_back(y_ind_map[y[i]]);
		H[y_ind_map[y[i]]].push_back(x_ind_map[x[i]]);
	}
	p.resize(x_ind);
	rep(i,x_ind){
		sort(all(G[i]));
		p[i]=y_ind_list[G[i][0]];
	}
	rep(i,y_ind) sort(all(H[i]));
	ans2=(ans2+(X-x_ind)*f(Y,2))%mod;
	ans2=(ans2+(Y-y_ind)*f(X,2))%mod;
	ans3=(ans3+(X-x_ind)*f(Y,3))%mod;
	ans3=(ans3+(Y-y_ind)*f(X,3))%mod;
	ans4=(ans4+(((X-x_ind)*(Y-y_ind))%mod)*(((X-1)*(Y-1))%mod))%mod;//L
	seg_tree seg1(x_ind),seg2(x_ind);
	rep(i,x_ind){
		seg1.set(i,p[i]-1);
		seg2.set(i,f(p[i]-1,2));
	}
	rep(i,y_ind+1){
		//cout<<ans2<<" "<<ans3<<" "<<ans4<<"\n";
		ll range;
		if(i==0) range=y_ind_list[0];
		else if(i==y_ind) range=Y-1-y_ind_list[y_ind-1];
		else range=y_ind_list[i]-1-y_ind_list[i-1];
		ans2=(ans2+seg1.sum()*range)%mod;
		ans3=(ans3+seg2.sum()*range)%mod;
		ans4=(ans4+((seg1.sum()*range)%mod)*(X-1))%mod;//L
		if(i==y_ind) break;
		ll l=0;
		rep(j,(int)(H[i].size())+1){
			ll r=x_ind;
			if(j!=(int)(H[i].size())) r=H[i][j];
			ll len;
			if(j==0) len=x_ind_list[r];
			else if(r==x_ind) len=X-1-x_ind_list[l-1];
			else len=x_ind_list[r]-x_ind_list[H[i][j-1]]-1;
			ans2=(ans2+seg1.query(l,r))%mod;
			ans3=(ans3+seg2.query(l,r))%mod;
			ans2=(ans2+f(len,2))%mod;
			ans3=(ans3+f(len,3))%mod;
			//L
			ll S=((Y-1)*(len-r+l))%mod;
			S=(S+seg1.query(l,r))%mod;
			ans4=(ans4+(len-1)*S)%mod;
			if(r!=x_ind){
				G_ind[r]++;
				ll tmp;
				if(G_ind[r]==(int)(G[r].size())){
					tmp=Y-1-y_ind_list[G[r][G_ind[r]-1]];
				}else{
					tmp=y_ind_list[G[r][G_ind[r]]]-1-y_ind_list[G[r][G_ind[r]-1]];
				}
				seg1.set(r,tmp-1);
				seg2.set(r,f(tmp-1,2));
			}
			l=r+1;
		}
	}
	ll ans=f(em,3);
	ll rev6=(mod+1)/6;
	//cout<<ans<<" "<<ans2<<" "<<ans3<<" "<<ans4<<"\n";
	ans2=(ans2*(em-2))%mod;
	ans=(ans-ans2*3)%mod;
	ans=(ans+ans3*2)%mod;
	ans=(ans+ans4*6)%mod;
	ans=(ans*rev6)%mod;
	cout<<(ans+mod)%mod<<"\n";
}
accuracy: 1
time_ms: 290
memory_kb: 44184
final_rank: 6

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
using uint = unsigned int;
using ull = unsigned long long;
#define rep(i,n) for(int i=0;i<int(n);i++)
#define rep1(i,n) for(int i=1;i<=int(n);i++)
#define per(i,n) for(int i=int(n)-1;i>=0;i--)
#define per1(i,n) for(int i=int(n);i>0;i--)
#define all(c) c.begin(),c.end()
#define si(x) int(x.size())
#define pb push_back
#define eb emplace_back
#define fs first
#define sc second
template<class T> using V = vector<T>;
template<class T> using VV = vector<vector<T>>;
template<class T,class U> bool chmax(T& x, U y){
	if(x<y){ x=y; return true; }
	return false;
}
template<class T,class U> bool chmin(T& x, U y){
	if(y<x){ x=y; return true; }
	return false;
}
template<class T> void mkuni(V<T>& v){sort(all(v));v.erase(unique(all(v)),v.end());}
template<class T> int lwb(const V<T>& v, const T& a){return lower_bound(all(v),a) - v.begin();}
template<class T>
V<T> Vec(size_t a) {
    return V<T>(a);
}
template<class T, class... Ts>
auto Vec(size_t a, Ts... ts) {
  return V<decltype(Vec<T>(ts...))>(a, Vec<T>(ts...));
}
template<class S,class T> ostream& operator<<(ostream& o,const pair<S,T> &p){
	return o<<"("<<p.fs<<","<<p.sc<<")";
}
template<class T> ostream& operator<<(ostream& o,const vector<T> &vc){
	o<<"{";
	for(const T& v:vc) o<<v<<",";
	o<<"}";
	return o;
}
constexpr ll TEN(int n) { return (n == 0) ? 1 : 10 * TEN(n-1); }

#ifdef LOCAL
#define show(x) cerr << "LINE" << __LINE__ << " : " << #x << " = " << (x) << endl
void dmpr(ostream& os){os<<endl;}
template<class T,class... Args>
void dmpr(ostream&os,const T&t,const Args&... args){
	os<<t<<" ~ ";
	dmpr(os,args...);
}
#define shows(...) cerr << "LINE" << __LINE__ << " : ";dmpr(cerr,##__VA_ARGS__)
#define dump(x) cerr << "LINE" << __LINE__ << " : " << #x << " = {";  \
	for(auto v: x) cerr << v << ","; cerr << "}" << endl;
#else
#define show(x) void(0)
#define dump(x) void(0)
#define shows(...) void(0)
#endif

template<class D> D divFloor(D a, D b){
	return a / b - (((a ^ b) < 0 && a % b != 0) ? 1 : 0);
}
template<class D> D divCeil(D a, D b) {
	return a / b + (((a ^ b) > 0 && a % b != 0) ? 1 : 0);
}

template<unsigned int mod_>
struct ModInt{
	using uint = unsigned int;
	using ll = long long;
	using ull = unsigned long long;

	constexpr static uint mod = mod_;

	uint v;
	ModInt():v(0){}
	ModInt(ll _v):v(normS(_v%mod+mod)){}
	explicit operator bool() const {return v!=0;}
	static uint normS(const uint &x){return (x<mod)?x:x-mod;}		// [0 , 2*mod-1] -> [0 , mod-1]
	static ModInt make(const uint &x){ModInt m; m.v=x; return m;}
	ModInt operator+(const ModInt& b) const { return make(normS(v+b.v));}
	ModInt operator-(const ModInt& b) const { return make(normS(v+mod-b.v));}
	ModInt operator-() const { return make(normS(mod-v)); }
	ModInt operator*(const ModInt& b) const { return make((ull)v*b.v%mod);}
	ModInt operator/(const ModInt& b) const { return *this*b.inv();}
	ModInt& operator+=(const ModInt& b){ return *this=*this+b;}
	ModInt& operator-=(const ModInt& b){ return *this=*this-b;}
	ModInt& operator*=(const ModInt& b){ return *this=*this*b;}
	ModInt& operator/=(const ModInt& b){ return *this=*this/b;}
	ModInt& operator++(int){ return *this=*this+1;}
	ModInt& operator--(int){ return *this=*this-1;}
	template<class T> friend ModInt operator+(T a, const ModInt& b){ return (ModInt(a) += b);}
	template<class T> friend ModInt operator-(T a, const ModInt& b){ return (ModInt(a) -= b);}
	template<class T> friend ModInt operator*(T a, const ModInt& b){ return (ModInt(a) *= b);}
	template<class T> friend ModInt operator/(T a, const ModInt& b){ return (ModInt(a) /= b);}
	ModInt pow(ll p) const {
		if(p<0) return inv().pow(-p);
		ModInt a = 1;
		ModInt x = *this;
		while(p){
			if(p&1) a *= x;
			x *= x;
			p >>= 1;
		}
		return a;
	}
	ModInt inv() const {		// should be prime
		return pow(mod-2);
	}
	// ll extgcd(ll a,ll b,ll &x,ll &y) const{
	// 	ll p[]={a,1,0},q[]={b,0,1};
	// 	while(*q){
	// 		ll t=*p/ *q;
	// 		rep(i,3) swap(p[i]-=t*q[i],q[i]);
	// 	}
	// 	if(p[0]<0) rep(i,3) p[i]=-p[i];
	// 	x=p[1],y=p[2];
	// 	return p[0];
	// }
	// ModInt inv() const {
	// 	ll x,y;
	// 	extgcd(v,mod,x,y);
	// 	return make(normS(x+mod));
	// }

	bool operator==(const ModInt& b) const { return v==b.v;}
	bool operator!=(const ModInt& b) const { return v!=b.v;}
	bool operator<(const ModInt& b) const { return v<b.v;}
	friend istream& operator>>(istream &o,ModInt& x){
		ll tmp;
		o>>tmp;
		x=ModInt(tmp);
		return o;
	}
	friend ostream& operator<<(ostream &o,const ModInt& x){ return o<<x.v;}
};
using mint = ModInt<1000000007>;
mint Choose(mint x, int k){
	static const mint i2 = mint(2).inv();
	static const mint i6 = mint(6).inv();
	if(k == 0) return 1;
	if(k == 1) return x;
	if(k == 2) return x*(x-1) * i2;
	if(k == 3) return x*(x-1)*(x-2) * i6;
	assert(0);
}

template<class D>
struct Node{
	using S = typename D::Monoid;
	using F = typename D::Action;
	S val;
	F act;
	int l,r;				// 10^9 でメモリ減らしたいならintに変える
	int lch,rch;
	Node(){}
	Node(ll l_,ll r_):act(D::id()),l(l_),r(r_),lch(-1),rch(-1){
		// should initialize val for [l,r)
		// ex. val = l + .. + r-1
		// ex. val.sz = r-l
		val = D::initialize(l,r);
	}
	Node(ll l_,ll r_,S val_):val(val_),act(D::id()),l(l_),r(r_),lch(-1),rch(-1){
		// OK. initlized with val (act = D::id())
	}
};
struct D{
	struct Monoid{
		mint s,ss;
		mint len;
		Monoid(){ *this = e(); }
		Monoid(mint v):s(v),ss(0),len(1){}
		Monoid(mint s_,mint ss_,mint len_):s(s_),ss(ss_),len(len_){}
		friend ostream& operator<<(ostream &o,const Monoid& x){ return o<<"{"<<x.s<<","<<x.ss<<","<<x.len<<"}";}
	};
	using Action = mint;
	const static Monoid e(){
		return Monoid(0,0,0);
	}
	const static Monoid op(const Monoid& x, const Monoid& y){
		Monoid z;
		z.s = x.s + y.s;
		z.ss = x.ss + x.s*y.len + y.ss;
		z.len = x.len + y.len;
		return z;
	}

	const static Action id(){
		return Action(0);
	}
	const static Action composite(const Action& f, const Action& g){
		// f \times g
		return f+g;
	}

	const static Monoid act(const Action& f, const Monoid& x){
		Monoid z;
		z.s = x.s + f * x.len;
		z.ss = x.ss + f * Choose(x.len,2);
		z.len = x.len;
		return z;
	}

	const static Monoid initialize(ll l, ll r){
		return Monoid(0,0,r-l);
	}
};


Node<D> pool[3000000];

template<class D>
struct lazyseg_dynamic{
	using S = typename D::Monoid;
	using F = typename D::Action;

	int I;
	template<class... Args>
	int makeNode(Args... args){
		pool[I] = Node<D>(args...);
		return I++;
	}

	int root;
	lazyseg_dynamic(){ I = 0; }
	lazyseg_dynamic(ll N){
		I = 0;
		ll n2 = 1;
		while(n2 < N) n2 *= 2;
		root = makeNode(0,n2);
	}

	S query(ll a, ll b){
		return query(a,b,root);
	}
	void apply(ll i, F f){
		apply(i,i+1,f);
	}
	void apply(ll a, ll b, F f){
		apply(a,b,f,root);
	}
	void assign(ll i, S x){
		assign(i,i+1,x,root);
	}
	private:
	S query(ll a, ll b, int n){
		ll l = pool[n].l, r = pool[n].r;
		if(b<=l || r<=a) return D::e();
		if(a<=l && r<=b) return pool[n].val;
		if(pool[n].lch == -1) pool[n].lch = makeNode(l,(l+r)/2);
		if(pool[n].rch == -1) pool[n].rch = makeNode((l+r)/2,r);
		propagate(l,r,n);
		return D::op(query(a,b,pool[n].lch) , query(a,b,pool[n].rch));
	}
	void apply(ll a, ll b, const F& f, int n){
		ll l = pool[n].l, r = pool[n].r;
		if(b<=l || r<=a) return;
		if(a<=l && r<=b){
			addlazy(n,f);
			return;
		}
		if(pool[n].lch == -1) pool[n].lch = makeNode(l,(l+r)/2);
		if(pool[n].rch == -1) pool[n].rch = makeNode((l+r)/2,r);
		propagate(l,r,n);
		apply(a,b,f,pool[n].lch);
		apply(a,b,f,pool[n].rch);
		pool[n].val = D::op(pool[pool[n].lch].val , pool[pool[n].rch].val);
	}
	void assign(ll a, ll b, const S& x, int n){
		ll l = pool[n].l, r = pool[n].r;
		if(b<=l || r<=a) return;
		if(a<=l && r<=b){
			// l = i, r = i+1
			pool[n].val = x;
			pool[n].act = D::id();
			return;
		}
		if(pool[n].lch == -1) pool[n].lch = makeNode(l,(l+r)/2);
		if(pool[n].rch == -1) pool[n].rch = makeNode((l+r)/2,r);
		propagate(l,r,n);
		assign(a,b,x,pool[n].lch);
		assign(a,b,x,pool[n].rch);
		pool[n].val = D::op(pool[pool[n].lch].val , pool[pool[n].rch].val);
	}
	void addlazy(int n, const F& f){
		pool[n].act = D::composite(f,pool[n].act);		// 上の階層の lazy ( = f) のほうがよりあと
		pool[n].val = D::act(f,pool[n].val);				// val は常に正しく
	}

	void propagate(ll l, ll r, int n){
		addlazy(pool[n].lch, pool[n].act);
		addlazy(pool[n].rch, pool[n].act);
		pool[n].act = D::id();
	}
};


int main(){
	cin.tie(0);
	ios::sync_with_stdio(false);		//DON'T USE scanf/printf/puts !!
	cout << fixed << setprecision(20);

	ll H,W,N; cin >> H >> W >> N;
	using P = pair<ll,ll>;
	V<P> ps(N);
	rep(i,N) cin >> ps[i].fs >> ps[i].sc;
	auto solve = [&](int k){
		map<int,V<int>> mp;
		rep(i,N) mp[ps[i].fs].pb(ps[i].sc);
		ll freeH = H - si(mp);
		ll fre = H*W - N;
		mint res = freeH * Choose(W,k);
		for(auto& [_,vs]: mp){
			vs.pb(-1); vs.pb(W);
			sort(all(vs));
			rep(i,si(vs)-1){
				ll len = vs[i+1]-vs[i]-1;
				res += Choose(len,k);
			}
		}
		if(k == 2) res *= fre-2;
		return res;
	};
	auto solveL = [&](){
		map<int,V<int>> mp;
		rep(i,N) mp[-ps[i].fs].pb(ps[i].sc);
		mp[+1] = {};
		ll ph = H;
		mint res;
		lazyseg_dynamic<D> seg(W);
		using S = D::Monoid;
		using F = D::Action;

		seg.apply(0,W,-1);
		for(auto& [h_,vs]: mp){
			ll h = -h_;
			if(h+1 <= ph-1){
				// [h+1,ph-1]
				ll n = ph-1 - (h+1) + 1;
				S z = seg.query(0,W);
				res += z.ss * n + Choose(z.len,2) * Choose(n+1,2);
			}
			if(h == -1) break;
			{
				// h
				seg.apply(0,W,ph-h);
				vs.pb(-1); vs.pb(W);
				sort(all(vs));
				rep(i,si(vs)-1){
					ll l = vs[i]+1, r = vs[i+1];
					res += seg.query(l,r).ss;
				}
				for(ll w: vs) if(w != -1 && w != W){
					seg.assign(w,S(-1));
				}
			}
			ph = h;
		}
		return res;
	};
	mint ans = Choose(H*W-N,3);
	rep(_,2){
		ans -= solve(2);
		ans += solve(3) * 2;
		rep(i,N) swap(ps[i].fs,ps[i].sc);
		swap(H,W);
	}
	rep(_,4){
		ans += solveL();
		rep(i,N){
			swap(ps[i].fs,ps[i].sc); ps[i].fs = W-1-ps[i].fs;
		}
		swap(H,W);
	}
	cout << ans << endl;
}
accuracy: 1
time_ms: 1810
memory_kb: 109436
final_rank: 11

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 1000000000000000000
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;



#define SIZE 300005

struct LOC{

	ll x,y;
};

ll X,Y,K;
ll rev_6;
ll ALL;
LOC loc[SIZE];
map<ll,ll> map_X,map_Y,rev_X,rev_Y;
vector<ll> vec_X,vec_Y,work_X,work_Y;
vector<ll> ROW[SIZE],COL[SIZE];


ll N;
ll BIT[3*SIZE];

void add(ll loc,ll value){

	BIT[loc] += value;
	BIT[loc] %= MOD;

	loc += loc & -loc;

	while(loc <= N){
		BIT[loc] += value;
		BIT[loc] %= MOD;
		loc += loc & -loc;
	}
}

ll getSum(ll loc){

	ll sum = BIT[loc];
	loc -= loc & -loc;

	while(loc > 0){
		sum += BIT[loc];
		sum %= MOD;
		loc -= loc & -loc;
	}
	return sum;
}

ll calc(ll left,ll right){
	return (getSum(right)-getSum(left-1)+MOD)%MOD;
}


ll extgcd(ll a,ll b,ll &x,ll &y){
	ll d = a;
	if(b != 0){
		d = extgcd(b,a%b,y,x);
		y -= (a/b)*x;
	}else{
		x = 1;
		y = 0;
	}
	return d;
}

ll mod_inverse(ll a,ll m){
    ll x,y;
    extgcd(a,m,x,y);
    return (m+x%m)%m;
}

ll nCk(ll num,ll k){

	if(k > num)return 0;

	if(k == 2){

		ll tmp = (num*(num-1))/2;
		return tmp%MOD;

	}else{ //k == 3

		ll tmp = (num*(num-1))%MOD;
		tmp *= (num-2);
		tmp %= MOD;
		tmp *= rev_6;

		return tmp%MOD;
	}
}

void PLUS(ll &A,ll B){

	A += B;
	A %= MOD;
}

void MINUS(ll &A,ll B){

	A -= B;
	if(A < 0){
		A += MOD;
	}
}

//計算補助関数
ll util_func(ll num){

	ll ret = nCk(num,2);
	ret *= (ALL-num)%MOD;
	ret %= MOD;

	ret += nCk(num,3);
	ret %= MOD;

	return ret;
}

ll calc_yoko(){

	//うさぎがいない行の数
	ll num = Y-(vec_Y.size()-2);

	ll ret = nCk(X,2); //2つが横で衝突
	ret *= (ALL-X)%MOD; //残る1つは他の行
	ret %= MOD;

	ret += nCk(X,3); //同じ行に3つ並ぶ
	ret %= MOD;

	ret *= num;
	ret %= MOD;

	for(ll i = 1; i < vec_Y.size()-1; i++){ //ダミー行は除く

		ll tmp_index = map_Y[vec_Y[i]];

		for(ll k = 0; k < ROW[tmp_index].size(); k++){
			if(k == 0){

				num = rev_X[ROW[tmp_index][k]]-1;
				PLUS(ret,util_func(num));
			}
			if(k > 0){

				num = rev_X[ROW[tmp_index][k]]-rev_X[ROW[tmp_index][k-1]]-1;
				PLUS(ret,util_func(num));
			}
			if(k == ROW[tmp_index].size()-1){

				num = X-rev_X[ROW[tmp_index][k]];
				PLUS(ret,util_func(num));
			}
		}
	}

	return ret;
}
ll calc_tate(){

	//うさぎがいない列の数
	ll num = X-(vec_X.size()-2);

	ll ret = nCk(Y,2); //2つが縦で衝突
	ret *= (ALL-Y)%MOD; //残る1つは他の列
	ret %= MOD;

	ret += nCk(Y,3); //同じ列に3つ並ぶ
	ret %= MOD;

	ret *= num;
	ret %= MOD;

	for(ll i = 1; i < vec_X.size()-1; i++){ //ダミー列は除く

		ll tmp_index = map_X[vec_X[i]];

		for(ll k = 0; k < COL[tmp_index].size(); k++){

			if(k == 0){

				num = rev_Y[COL[tmp_index][k]]-1;
				PLUS(ret,util_func(num));
			}
			if(k > 0){

				num = rev_Y[COL[tmp_index][k]]-rev_Y[COL[tmp_index][k-1]]-1;
				PLUS(ret,util_func(num));
			}
			if(k == COL[tmp_index].size()-1){

				num = Y-rev_Y[COL[tmp_index][k]];
				PLUS(ret,util_func(num));
			}
		}
	}

	return ret;
}


ll get_next(ll row_index,ll value){

	ll ret = map_X[X+1];

	ll left = 0,right = ROW[row_index].size()-1,mid = (left+right)/2;

	while(left <= right){

		if(ROW[row_index][mid] > value){

			ret = ROW[row_index][mid];
			right = mid-1;
		}else{

			left = mid+1;
		}
		mid = (left+right)/2;
	}

	return ret;
}

ll calc_L(){

	N = work_Y.size();

	for(ll i = 0; i <= N; i++){

		BIT[i] = 0;
	}

	//縦の空白区間を加算
	for(ll i = 1; i < vec_Y.size(); i++){
		ll left = map_Y[vec_Y[i-1]]+1;
		ll right = map_Y[vec_Y[i]]-1;

		if(left > right)continue;

		ll add_value = ((X-1)*(vec_Y[i]-vec_Y[i-1]-1))%MOD;

		add(left+1,add_value); //left == rightであるはず(1が先頭インデックス)
	}

	//少なくとも1匹うさぎがいる行について、最初のうさぎまでの区間長を求める
	ll tmp_index,num;

	for(ll i = 1; i < vec_Y.size()-1; i++){

		tmp_index = map_Y[vec_Y[i]];
		num = rev_X[ROW[tmp_index][0]]; //一番左にいるうさぎの位置

		if(num >= 3){
			add(tmp_index+1,num-2);
		}
	}

	ll ret = 0;
	ll len,tmp;

	if(vec_X[1] > 1){ //最初に空白地帯あり

		len = vec_X[1]-1;
		tmp = calc(1,N);

		tmp *= len;
		tmp %= MOD;

		tmp *= Y-1;
		tmp %= MOD;

		PLUS(ret,tmp);
	}

	ll L,R;
	ll current;

	for(ll i = 1; i < vec_X.size(); i++){

		tmp_index = map_X[vec_X[i-1]];

		if(i > 1){ //★★列vec_X[i-1]を抜けた直後の状態を作る★★

			for(ll k = 0; k < COL[tmp_index].size(); k++){

				ll tmp_y = COL[tmp_index][k];
				ll next_x = get_next(tmp_y,tmp_index);

				tmp = rev_X[next_x]-rev_X[tmp_index]-1;

				if(tmp >= 2){
					ll work = calc(tmp_y+1,tmp_y+1);
					add(tmp_y+1,-work);
					add(tmp_y+1,tmp-1);
				}
			}
		}

		if(i > 1 && vec_X[i]-vec_X[i-1] > 1){ //間に空白列あり

			len = (vec_X[i]-1)-(vec_X[i-1]+1)+1;
			tmp = calc(1,N);

			tmp *= len;
			tmp %= MOD;

			tmp *= Y-1;
			tmp %= MOD;

			PLUS(ret,tmp);
		}
		if(i == vec_X.size()-1)break;

		//今回の列で、うさぎがいる行を0にする
		current = map_X[vec_X[i]];

		for(ll k = 0; k < COL[current].size(); k++){

			tmp = COL[current][k];
			ll work = calc(tmp+1,tmp+1);
			add(tmp+1,-work);
		}

		//今回の列分を加算
		for(ll k = 0; k < COL[current].size(); k++){

			if(k == 0){

				L = 1;
				R = COL[current][k];

				if(rev_Y[R] >= 3){
					num = calc(L+1,R);

					num *= (rev_Y[R]-2);
					num %= MOD;

					PLUS(ret,num);
				}
			}
			if(k > 0){

				L = COL[current][k-1];
				R = COL[current][k];

				if(rev_Y[R]-rev_Y[L] >= 3){
					num = calc(L+2,R);

					num *= (rev_Y[R]-rev_Y[L]-2);
					num %= MOD;

					PLUS(ret,num);
				}
			}
			if(k == COL[current].size()-1){

				L = COL[current][k];
				R = map_Y[Y+1];

				if(Y+1-rev_Y[L] >= 3){

					num = calc(L+2,R);
					num *= (Y-rev_Y[L]-1);
					num %= MOD;

					PLUS(ret,num);
				}
			}
		}
	}

	return ret;
}

int main(){

	rev_6 = mod_inverse(6,MOD); //6の逆元

	scanf("%lld %lld %lld",&X,&Y,&K);

	//★★盤の周囲をダミーセルで囲う★★
	vec_X.push_back(0);
	vec_X.push_back(X+1);

	work_X.push_back(0);
	work_X.push_back(1);
	work_X.push_back(X+1);

	vec_Y.push_back(0);
	vec_Y.push_back(Y+1);

	work_Y.push_back(0);
	work_Y.push_back(1);
	work_Y.push_back(Y+1);

	for(ll i = 0; i < K; i++){

		scanf("%lld %lld",&loc[i].x,&loc[i].y);
		//ダミーセルを使うため、x,yに+1する
		loc[i].x++;
		loc[i].y++;

		vec_X.push_back(loc[i].x);
		vec_Y.push_back(loc[i].y);

		for(ll a = 0; a <= 1; a++){ //セグ木計算用に、余分に値を作る

			work_X.push_back(loc[i].x+a);
			work_Y.push_back(loc[i].y+a);
		}
	}

	//使用列・行把握のため
	sort(vec_X.begin(),vec_X.end());
	vec_X.erase(unique(vec_X.begin(),vec_X.end()),vec_X.end());

	sort(vec_Y.begin(),vec_Y.end());
	vec_Y.erase(unique(vec_Y.begin(),vec_Y.end()),vec_Y.end());

	/*座標圧縮*/
	sort(work_X.begin(),work_X.end());
	work_X.erase(unique(work_X.begin(),work_X.end()),work_X.end());

	for(ll i = 0; i < work_X.size(); i++){

		map_X[work_X[i]] = i;
		rev_X[i] = work_X[i];
	}

	sort(work_Y.begin(),work_Y.end());
	work_Y.erase(unique(work_Y.begin(),work_Y.end()),work_Y.end());

	for(ll i = 0; i < work_Y.size(); i++){

		map_Y[work_Y[i]] = i;
		rev_Y[i] = work_Y[i];
	}
	//うさぎの座標【圧縮後】を各行、各列に振り分ける
	for(ll i = 0; i < K; i++){

		ROW[map_Y[loc[i].y]].push_back(map_X[loc[i].x]);
		COL[map_X[loc[i].x]].push_back(map_Y[loc[i].y]);
	}

	for(ll i = 1; i < vec_X.size()-1; i++){

		ll tmp_index = map_X[vec_X[i]];

		sort(COL[tmp_index].begin(),COL[tmp_index].end());
	}
	for(ll i = 1; i < vec_Y.size()-1; i++){

		ll tmp_index = map_Y[vec_Y[i]];
		sort(ROW[tmp_index].begin(),ROW[tmp_index].end());
	}

	ALL = X*Y-K;

	ll ans = nCk(ALL%MOD,3);

	//横の衝突あり
	ll num_yoko = calc_yoko();

	//縦の衝突あり
	ll num_tate = calc_tate();

	//縦と横の衝突あり(L字型)
	ll num_L = calc_L();

	MINUS(ans,num_yoko);
	MINUS(ans,num_tate);
	PLUS(ans,num_L);

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

	return 0;
}
accuracy: 1
time_ms: 1140
memory_kb: 80964
final_rank: 9

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 1000000000000000000
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;



#define SIZE 300005

struct LOC{

	ll x,y;
};

ll X,Y,K;
ll rev_6;
ll ALL;
LOC loc[SIZE];
map<ll,ll> map_X,map_Y,rev_X,rev_Y;
vector<ll> vec_X,vec_Y,work_X,work_Y;
vector<ll> ROW[SIZE],COL[SIZE];


ll N;
ll BIT[3*SIZE];

void add(ll loc,ll value){

	BIT[loc] += value;
	BIT[loc] %= MOD;

	loc += loc & -loc;

	while(loc <= N){
		BIT[loc] += value;
		BIT[loc] %= MOD;
		loc += loc & -loc;
	}
}

ll getSum(ll loc){

	ll sum = BIT[loc];
	loc -= loc & -loc;

	while(loc > 0){
		sum += BIT[loc];
		sum %= MOD;
		loc -= loc & -loc;
	}
	return sum;
}

ll calc(ll left,ll right){
	return (getSum(right)-getSum(left-1)+MOD)%MOD;
}


ll extgcd(ll a,ll b,ll &x,ll &y){
	ll d = a;
	if(b != 0){
		d = extgcd(b,a%b,y,x);
		y -= (a/b)*x;
	}else{
		x = 1;
		y = 0;
	}
	return d;
}

ll mod_inverse(ll a,ll m){
    ll x,y;
    extgcd(a,m,x,y);
    return (m+x%m)%m;
}

ll nCk(ll num,ll k){

	if(k > num)return 0;

	if(k == 2){

		ll tmp = (num*(num-1))/2;
		return tmp%MOD;

	}else{ //k == 3

		ll tmp = (num*(num-1))%MOD;
		tmp *= (num-2);
		tmp %= MOD;
		tmp *= rev_6;

		return tmp%MOD;
	}
}

void PLUS(ll &A,ll B){

	A += B;
	A %= MOD;
}

void MINUS(ll &A,ll B){

	A -= B;
	if(A < 0){
		A += MOD;
	}
}

//計算補助関数
ll util_func(ll num){

	ll ret = nCk(num,2);
	ret *= (ALL-num)%MOD;
	ret %= MOD;

	ret += nCk(num,3);
	ret %= MOD;

	return ret;
}

ll calc_yoko(){

	//うさぎがいない行の数
	ll num = Y-(vec_Y.size()-2);

	ll ret = nCk(X,2); //2つが横で衝突
	ret *= (ALL-X)%MOD; //残る1つは他の行
	ret %= MOD;

	ret += nCk(X,3); //同じ行に3つ並ぶ
	ret %= MOD;

	ret *= num;
	ret %= MOD;

	//printf("yoko ret:%lld\n",ret);

	for(ll i = 1; i < vec_Y.size()-1; i++){ //ダミー行は除く

		ll tmp_index = map_Y[vec_Y[i]];

		for(ll k = 0; k < ROW[tmp_index].size(); k++){
			if(k == 0){

				num = rev_X[ROW[tmp_index][k]]-1;
				PLUS(ret,util_func(num));
			}
			if(k > 0){

				num = rev_X[ROW[tmp_index][k]]-rev_X[ROW[tmp_index][k-1]]-1;
				PLUS(ret,util_func(num));
			}
			if(k == ROW[tmp_index].size()-1){

				num = X-rev_X[ROW[tmp_index][k]];
				PLUS(ret,util_func(num));
			}
		}
	}

	return ret;
}
ll calc_tate(){

	//うさぎがいない列の数
	ll num = X-(vec_X.size()-2);

	ll ret = nCk(Y,2); //2つが縦で衝突
	ret *= (ALL-Y)%MOD; //残る1つは他の列
	ret %= MOD;

	ret += nCk(Y,3); //同じ列に3つ並ぶ
	ret %= MOD;

	ret *= num;
	ret %= MOD;

	//printf("tate ret:%lld\n",ret);

	for(ll i = 1; i < vec_X.size()-1; i++){ //ダミー列は除く

		ll tmp_index = map_X[vec_X[i]];

		for(ll k = 0; k < COL[tmp_index].size(); k++){

			if(k == 0){

				num = rev_Y[COL[tmp_index][k]]-1;
				PLUS(ret,util_func(num));
			}
			if(k > 0){

				num = rev_Y[COL[tmp_index][k]]-rev_Y[COL[tmp_index][k-1]]-1;
				PLUS(ret,util_func(num));
			}
			if(k == COL[tmp_index].size()-1){

				num = Y-rev_Y[COL[tmp_index][k]];
				PLUS(ret,util_func(num));
			}
		}
	}

	return ret;
}


ll get_next(ll row_index,ll value){

	ll ret = map_X[X+1];

	ll left = 0,right = ROW[row_index].size()-1,mid = (left+right)/2;

	while(left <= right){

		if(ROW[row_index][mid] > value){

			ret = ROW[row_index][mid];
			right = mid-1;
		}else{

			left = mid+1;
		}
		mid = (left+right)/2;
	}

	return ret;
}

ll calc_L(){

	N = work_Y.size();

	for(ll i = 0; i <= N; i++){

		BIT[i] = 0;
	}

	//縦の空白区間を加算
	for(ll i = 1; i < vec_Y.size(); i++){
		ll left = map_Y[vec_Y[i-1]]+1;
		ll right = map_Y[vec_Y[i]]-1;

		if(left > right)continue;

		ll add_value = ((X-1)*(vec_Y[i]-vec_Y[i-1]-1))%MOD;

		//printf("left:%lld right:%lld len:%lld add:%lld\n",left,right,vec_Y[i]-vec_Y[i-1]-1,add);

		add(left+1,add_value); //left == rightであるはず
		//printf("left:%lld right:%lld %lld加算\n",left,right,X-1);
	}

	//少なくとも1匹うさぎがいる行について、最初のうさぎまでの区間長を求める
	ll tmp_index,num;

	for(ll i = 1; i < vec_Y.size()-1; i++){

		tmp_index = map_Y[vec_Y[i]];
		num = rev_X[ROW[tmp_index][0]]; //一番左にいるうさぎの位置

		//printf("tmp_index:%lld num:%lld\n",tmp_index,num);

		if(num >= 3){
			add(tmp_index+1,num-2);
		}
	}

	ll ret = 0;
	ll len,tmp;

	if(vec_X[1] > 1){ //最初に空白地帯あり

		//printf("最初に空白地帯\n");

		len = vec_X[1]-1;
		tmp = calc(1,N);

		tmp *= len;
		tmp %= MOD;

		tmp *= Y-1;
		tmp %= MOD;

		//printf("tmp:%lld\n",tmp);

		PLUS(ret,tmp);
	}

	ll L,R;
	ll current;

	for(ll i = 1; i < vec_X.size(); i++){

		tmp_index = map_X[vec_X[i-1]];

		if(i > 1){ //★★列vec_X[i-1]を抜けた直後の状態を作る★★

			//printf("抜けた状態を作る\n");

			for(ll k = 0; k < COL[tmp_index].size(); k++){

				ll tmp_y = COL[tmp_index][k];
				//printf("%lldにうさぎがいた\n",rev_Y[tmp_y]);
				ll next_x = get_next(tmp_y,tmp_index);

				//printf("tmp_index:%lld next_x:%lld\n",tmp_index,next_x);
				tmp = rev_X[next_x]-rev_X[tmp_index]-1;

				//printf("rev_X[next_x]:%lld rev_X[tmp_index]:%lld tmp-1:%lld\n",rev_X[next_x],rev_X[tmp_index],tmp-1);
				if(tmp >= 2){
					ll work = calc(tmp_y+1,tmp_y+1);
					//printf("tmp_y+1:%lld work:%lld\n",tmp_y+1,work);
					add(tmp_y+1,-work);
					add(tmp_y+1,tmp-1);
				}
			}
		}

		if(i > 1 && vec_X[i]-vec_X[i-1] > 1){ //間に空白列あり

			//printf("vec_X[i]:%lld くうはくあり\n",vec_X[i]);

			len = (vec_X[i]-1)-(vec_X[i-1]+1)+1;
			tmp = calc(1,N);

			//printf("i:%lld\n",i);
			//printf("len:%lld tmp:%lld\n",len,tmp);

			tmp *= len;
			tmp %= MOD;

			tmp *= Y-1;
			tmp %= MOD;

			//printf("tmp:%lld\n",tmp);

			PLUS(ret,tmp);
		}
		if(i == vec_X.size()-1)break;

		//今回の列で、うさぎがいる行を0にする
		current = map_X[vec_X[i]];

		for(ll k = 0; k < COL[current].size(); k++){

			tmp = COL[current][k];
			ll work = calc(tmp+1,tmp+1);
			//printf("tmp+1:%Lld work:%lld\n",tmp+1,work);
			add(tmp+1,-work);
		}

		//今回の列分を加算
		for(ll k = 0; k < COL[current].size(); k++){

			if(k == 0){

				L = 1;
				R = COL[current][k];

				if(rev_Y[R] >= 3){
					num = calc(L+1,R);

					num *= (rev_Y[R]-2);
					num %= MOD;

					//printf("最初:num:%lld\n",num);

					PLUS(ret,num);
				}
			}
			if(k > 0){

				L = COL[current][k-1];
				R = COL[current][k];

				if(rev_Y[R]-rev_Y[L] >= 3){
					num = calc(L+2,R);

					num *= (rev_Y[R]-rev_Y[L]-2);
					num %= MOD;

					//printf("中間 num:%lld\n",num);

					PLUS(ret,num);
				}
			}
			if(k == COL[current].size()-1){

				L = COL[current][k];
				R = map_Y[Y+1];

				if(Y+1-rev_Y[L] >= 3){
					num = calc(L+2,R);

					//printf("ラスト num:%lld\n",num);

					num *= (Y-rev_Y[L]-1);
					//printf("区間長:%lld\n",Y-rev_Y[L]-1);
					num %= MOD;

					//printf("ラスト:%lld\n",num);

					PLUS(ret,num);
				}
			}
		}
	}

	return ret;
}

int main(){

	rev_6 = mod_inverse(6,MOD); //6の逆元

	scanf("%lld %lld %lld",&X,&Y,&K);

	//★★盤の周囲をダミーセルで囲う★★
	vec_X.push_back(0);
	vec_X.push_back(X+1);

	work_X.push_back(0);
	work_X.push_back(1);
	work_X.push_back(X+1);

	vec_Y.push_back(0);
	vec_Y.push_back(Y+1);

	work_Y.push_back(0);
	work_Y.push_back(1);
	work_Y.push_back(Y+1);

	for(ll i = 0; i < K; i++){

		scanf("%lld %lld",&loc[i].x,&loc[i].y);
		//ダミーセルを使うため、x,yに+1する
		loc[i].x++;
		loc[i].y++;

		vec_X.push_back(loc[i].x);
		vec_Y.push_back(loc[i].y);

		for(ll a = 0; a <= 1; a++){ //セグ木計算用に、余分に値を作る

			work_X.push_back(loc[i].x+a);
			work_Y.push_back(loc[i].y+a);
		}
	}

	//使用列・行把握のため
	sort(vec_X.begin(),vec_X.end());
	vec_X.erase(unique(vec_X.begin(),vec_X.end()),vec_X.end());

	sort(vec_Y.begin(),vec_Y.end());
	vec_Y.erase(unique(vec_Y.begin(),vec_Y.end()),vec_Y.end());

	/*座標圧縮*/
	sort(work_X.begin(),work_X.end());
	work_X.erase(unique(work_X.begin(),work_X.end()),work_X.end());

	for(ll i = 0; i < work_X.size(); i++){

		map_X[work_X[i]] = i;
		//printf("i:%lld :%lld\n",i,work_X[i]);
		rev_X[i] = work_X[i];
	}
	//printf("\n");

	sort(work_Y.begin(),work_Y.end());
	work_Y.erase(unique(work_Y.begin(),work_Y.end()),work_Y.end());

	for(ll i = 0; i < work_Y.size(); i++){

		map_Y[work_Y[i]] = i;
		//printf("i:%lld :%lld\n",i,work_Y[i]);
		rev_Y[i] = work_Y[i];
	}

	//printf("\n");

	//うさぎの座標【圧縮後】を各行、各列に振り分ける
	for(ll i = 0; i < K; i++){

		ROW[map_Y[loc[i].y]].push_back(map_X[loc[i].x]);
		COL[map_X[loc[i].x]].push_back(map_Y[loc[i].y]);
	}

	for(ll i = 1; i < vec_X.size()-1; i++){

		ll tmp_index = map_X[vec_X[i]];

		sort(COL[tmp_index].begin(),COL[tmp_index].end());
	}
	for(ll i = 1; i < vec_Y.size()-1; i++){

		ll tmp_index = map_Y[vec_Y[i]];
		sort(ROW[tmp_index].begin(),ROW[tmp_index].end());
	}

	ALL = X*Y-K;

	ll ans = nCk(ALL%MOD,3);

	//横の衝突あり
	ll num_yoko = calc_yoko();

	//縦の衝突あり
	ll num_tate = calc_tate();

	//縦と横の衝突あり(L字型)
	ll num_L = calc_L();

	//printf("all:%lld num_L:%lld 横:%lld 縦:%lld\n",ans,num_L,num_yoko,num_tate);

	MINUS(ans,num_yoko);
	MINUS(ans,num_tate);
	PLUS(ans,num_L);

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

	return 0;
}
accuracy: 1
time_ms: 1170
memory_kb: 80960
final_rank: 10

code_snippet: #include <bits/stdc++.h>
#define rep(i,n) for(int i = 0; i < n; i++)
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
typedef pair<ll,P> PP;
const long long int MOD = 1000000007;
const long long int INF = 1000000000;

ll x, y, k, al;
ll idsize;
map<ll,ll> toid;
vector<P> xy, yx;
ll ans = 0;
ll nod[200001];
ll bit[200001];
vector<ll> vec[200001];
ll forvec[200001];

ll sum(ll i){
	ll s = 0;
	while(i > 0){
		s = (s+bit[i]+MOD)%MOD;
		i -= i&-i;
	}
	return s;
}

void add(ll i, ll x){
	while(i <= idsize){
        bit[i] = (bit[i]+x+MOD)%MOD;
		i += i&-i;
	}
}

ll mod_pow(ll a, ll b){
    ll ret = 1;
    while(b){
        if(b&1) ret = ret*a%MOD;
        a = a*a%MOD;
        b /= 2;
    }
    return ret;
}

int main(){
    cin >> x >> y >> k;
    al = x*y%MOD;
    if(x*y-k <= 2){
        cout << 0 << endl;
        return 0;
    }
    rep(i,k){
        ll xx, yy;
        cin >> xx >> yy;
        xy.push_back(P(xx,yy));
        yx.push_back(P(yy,xx));
    }
    sort(xy.begin(),xy.end());
    sort(yx.begin(),yx.end());
    ans = (al-k+MOD)*(al-k-1+MOD)%MOD;
    ans = ans*(al-k-2+MOD)%MOD;
    ans = ans*mod_pow(6,MOD-2)%MOD;
    //cout << ans << endl;
    {
        ll cnt = 0;
        ll ad;
        ll ad2;
        rep(i,xy.size()){
            if(i == 0 || xy[i].first != xy[i-1].first){
                cnt++;
            }
            if(i == 0 || xy[i].first != xy[i-1].first){
                if(xy[i].second-1 > 0){
                    ad = ((xy[i].second)*(xy[i].second-1)/2)%MOD;
                    ad = ad*(al-k-xy[i].second+5*MOD)%MOD;
                    ans = (ans-ad+MOD)%MOD;
                }
                ad2 = xy[i].second*(xy[i].second-1)%MOD;
                ad2 = ad2*(xy[i].second-2)%MOD;
                ad2 = ad2*mod_pow(6,MOD-2)%MOD;
                ans = (ans-ad2+MOD)%MOD;
            } else{
                if(xy[i].second-xy[i-1].second-2 > 0){
                    ad = ((xy[i].second-xy[i-1].second-1)*(xy[i].second-xy[i-1].second-2)/2)%MOD;
                    ad = ad*(al-k-(xy[i].second-xy[i-1].second-1)+5*MOD)%MOD;
                    ans = (ans-ad+MOD)%MOD;
                }
                ad2 = (xy[i].second-xy[i-1].second-1)*(xy[i].second-xy[i-1].second-2)%MOD;
                ad2 = ad2*(xy[i].second-xy[i-1].second-3)%MOD;
                ad2 = ad2*mod_pow(6,MOD-2)%MOD;
                ans = (ans-ad2+MOD)%MOD;
            }
            if(i == xy.size()-1 || xy[i].first != xy[i+1].first){
                if(y-xy[i].second-2 > 0){
                    ad = ((y-xy[i].second-1)*(y-xy[i].second-2)/2)%MOD;
                    ad = ad*(al-k-(y-xy[i].second-1))%MOD;
                    ans = (ans-ad+MOD)%MOD;
                }
                ad2 = (y-xy[i].second-1)*(y-xy[i].second-2)%MOD;
                ad2 = ad2*(y-xy[i].second-3)%MOD;
                ad2 = ad2*mod_pow(6,MOD-2);
                ans = (ans-ad2+MOD)%MOD;
            }
        }
        //cout << ans << " " << cnt << endl;
        ad = (y*(y-1)/2)%MOD;
        ad = ad*(x-cnt)%MOD;
        ad = ad*(al-k-y+5*MOD)%MOD;
        ans = (ans-ad+MOD)%MOD;
        ad2 = y*(y-1)%MOD;
        ad2 = ad2*(y-2)%MOD;
        ad2 = ad2*(x-cnt)%MOD;
        ad2 = ad2*mod_pow(6,MOD-2)%MOD;
        ans = (ans-ad2+MOD)%MOD;
        //cout << ans << endl;
        
        cnt = 0;
        rep(i,yx.size()){
            if(i == 0 || yx[i].first != yx[i-1].first){
                cnt++;
            }
            if(i == 0 || yx[i].first != yx[i-1].first){
                if(yx[i].second-1 > 0){
                    ad = (yx[i].second*(yx[i].second-1)/2)%MOD;
                    ad = ad*(al-k-yx[i].second+5*MOD)%MOD;
                    ans = (ans-ad+MOD)%MOD;
                }
                ad2 = yx[i].second*(yx[i].second-1)%MOD;
                ad2 = ad2*(yx[i].second-2)%MOD;
                ad2 = ad2*mod_pow(6,MOD-2)%MOD;
                ans = (ans-ad2+MOD)%MOD;
            } else{
                if(yx[i].second-yx[i-1].second-2 > 0){
                    ad = ((yx[i].second-yx[i-1].second-1)*(yx[i].second-yx[i-1].second-2)/2)%MOD;
                    ad = ad*(al-k-(yx[i].second-yx[i-1].second-1)+5*MOD)%MOD;
                    ans = (ans-ad+MOD)%MOD;
                }
                ad2 = (yx[i].second-yx[i-1].second-1)*(yx[i].second-yx[i-1].second-2)%MOD;
                ad2 = ad2*(yx[i].second-yx[i-1].second-3)%MOD;
                ad2 = ad2*mod_pow(6,MOD-2)%MOD;
                ans = (ans-ad2+MOD)%MOD;
            }
            //cout << ans << endl;
            if(i == yx.size()-1 || yx[i].first != yx[i+1].first){
                if(x-yx[i].second-2 > 0){
                    ad = ((x-yx[i].second-1)*(x-yx[i].second-2)/2)%MOD;
                    ad = ad*(al-k-(x-yx[i].second-1)+5*MOD)%MOD;
                    ans = (ans-ad+MOD)%MOD;
                }
                ad2 = (x-yx[i].second-1)*(x-yx[i].second-2)%MOD;
                ad2 = ad2*(x-yx[i].second-3)%MOD;
                ad2 = ad2*mod_pow(6,MOD-2)%MOD;
                ans = (ans-ad2+MOD)%MOD;
            }
            //cout << ans << endl;
        }
        //cout << ans << " " << cnt << endl;
        ad = (x*(x-1)/2)%MOD;
        ad = ad*(y-cnt)%MOD;
        ad = ad*(al-k-x+2*MOD)%MOD;
        ans = (ans-ad+MOD)%MOD;
        ad2 = x*(x-1)%MOD;
        ad2 = ad2*(x-2)%MOD;
        ad2 = ad2*(y-cnt)%MOD;
        ad2 = ad2*mod_pow(6,MOD-2)%MOD;
        ans = (ans-ad2+MOD)%MOD;
    }
    //cout << ans << endl;
    {
        ll now = -1;
        ll last = -1;
        rep(i,xy.size()){
            if(xy[i].first > now+1){
                ll val = y*(xy[i].first-now-1)%MOD;
                nod[idsize] = val;
                idsize++;
            }
            if(xy[i].first == now){
                //que[idsize-1].push(xy[i].second-last-1);
                vec[idsize-1].push_back(xy[i].second-last-1);
                last = xy[i].second;
            } else{
                nod[idsize] = xy[i].second;
                toid[xy[i].first] = idsize;
                idsize++;
                last = xy[i].second;
            }
            if(i == xy.size()-1 || xy[i].first != xy[i+1].first){
                //que[idsize-1].push(y-last-1);
                vec[idsize-1].push_back(y-last-1);
            }
            now = xy[i].first;
        }
        if(x > now+1){
            ll val = y*(x-now-1)%MOD;
            nod[idsize] = val;
            idsize++;
        }
    }
    //cout << "nod:";
    rep(i,idsize){
        //cout << nod[i] << " ";
        add(i+1,nod[i]);
    }
    //cout << endl;
    {
        ll nowx = -1, nowy = -1;
        rep(i,yx.size()){
            //cout << yx[i].first << " " << yx[i].second << endl;
            ll id = toid[yx[i].second];
            if(yx[i].first > nowy+1){
                //cout << "___1___" << endl;
                ll val = yx[i].first-nowy-1;
                val = val*(x-1)%MOD;
                val = val*(sum(idsize)-x+MOD)%MOD;
                ans = (ans+val)%MOD;
            }
            if(yx[i].first != nowy){
                //cout << "___2___" << endl;
                ll val = yx[i].second-1;
                if(val > 0){
                    //cout << "_2_" << endl;
                    val = val*(sum(id)-yx[i].second+MOD)%MOD;
                    ans = (ans+val)%MOD;
                }
                nowy = yx[i].first;
                nowx = yx[i].second;
            } else{
                //cout << "___3___" << endl;
                ll val = yx[i].second-nowx-2;
                if(val > 0){
                    val = val*(sum(id)-sum(toid[nowx]+1)-val-1+5*MOD)%MOD;
                    ans = (ans+val)%MOD;
                }
                nowx = yx[i].second;
            }
            if(i == yx.size()-1 || yx[i].first != yx[i+1].first){
                //cout << "___4___" << endl;
                ll val = x-nowx-2;
                if(val > 0){
                    val = val*(sum(idsize)-sum(toid[nowx]+1)-val-1+5*MOD)%MOD;
                    ans = (ans+val)%MOD;
                }
            }
            //ll q = que[id].front();
            ll q = vec[id][forvec[id]];
            forvec[id]++;
            //que[id].pop();
            ll dif = (q-nod[id]+MOD)%MOD;
            add(id+1,dif);
            nod[id] = q;
        }
        ll val = y-nowy-1;
        //cout << "val " << val <<  " " << sum(idsize) << endl;
        val = val*(x-1)%MOD;
        val = val*(sum(idsize)-x+MOD)%MOD;
        ans = (ans+val)%MOD;
    }
    cout << ans << endl;
}
accuracy: 1
time_ms: 200
memory_kb: 24836
final_rank: 2

code_snippet: #include<stdio.h>
#include<algorithm>
#include<set>
#include<vector>
using namespace std;
long long mod=1000000007;
long long os=166666668;
long long ot=500000004;
int p[110000];
int q[110000];
int xz[110000];
int yz[110000];
vector<int>Y[110000];
vector<int>X[110000];
int next[110000];
long long segtree[262144];
long long query(int a,int b,int c,int d,int e){
	if(c>d)return 0;
	if(d<a||b<c)return 0;
	if(c<=a&&b<=d)return segtree[e];
	return query(a,(a+b)/2,c,d,e*2)+query((a+b)/2+1,b,c,d,e*2+1);
}
void update(int a,int b){
	a+=131072;
	segtree[a]=b;
	a/=2;
	while(a){
		segtree[a]=segtree[a*2]+segtree[a*2+1];
		a/=2;
	}
}
int sum[110000];
int main(){
	int a,b,n;
	scanf("%d%d%d",&a,&b,&n);
	if((long long)a*b-n<3){
		printf("0\n");return 0;
	}
	long long ms=((long long)a*b-n)%mod;
	for(int i=0;i<n;i++){
		scanf("%d%d",p+i,q+i);
		xz[i]=p[i];yz[i]=q[i];
	}
	std::sort(xz,xz+n);
	std::sort(yz,yz+n);
	for(int i=0;i<n;i++){
		Y[lower_bound(xz,xz+n,p[i])-xz].push_back(q[i]);
		X[lower_bound(yz,yz+n,q[i])-yz].push_back(p[i]);
	}
	for(int i=0;i<n;i++){
		std::sort(X[i].begin(),X[i].end());
		std::sort(Y[i].begin(),Y[i].end());
	}
	int xs=0;int ys=0;
	for(int i=0;i<n;i++){
		if(i==0||xz[i]!=xz[i-1]){
			sum[i+1]=1;
			xs++;
		}
		if(i==0||yz[i]!=yz[i-1])ys++;
	}
	for(int i=1;i<=n;i++)sum[i]+=sum[i-1];
	
	long long ret=ms*(ms-1)%mod*(ms-2)%mod*os%mod;
	//printf("%lld\n",ret);
	long long nega=0;
	nega=(nega+(long long)(b-ys)*a%mod*(a-1)%mod*ot%mod*(ms-2))%mod;
	nega=(nega+(long long)(a-xs)*b%mod*(b-1)%mod*ot%mod*(ms-2))%mod;
	ret=(ret+(long long)(b-ys)*a%mod*(a-1)%mod*(a-2)%mod*os*2)%mod;
	ret=(ret+(long long)(a-xs)*b%mod*(b-1)%mod*(b-2)%mod*os*2)%mod;
	for(int i=0;i<n;i++){
		if(i&&xz[i]==xz[i-1])continue;
		int at=0;
		for(int j=0;j<Y[i].size();j++){
			int to=Y[i][j];
			if(to-at>1){
				long long len=to-at;
				nega=(nega+len*(len-1)%mod*ot%mod*(ms-2))%mod;
			}
			if(to-at>2){
				long long len=to-at;
				ret=(ret+len*(len-1)%mod*(len-2)%mod*os%mod*2)%mod;
			}
			at=to+1;
		}
		if(at<b-1){
			long long len=b-at;
			nega=(nega+len*(len-1)%mod*ot%mod*(ms-2))%mod;
		}
		if(at<b-2){
			long long len=b-at;
			ret=(ret+len*(len-1)%mod*(len-2)%mod*os%mod*2)%mod;
		}
	}
	for(int i=0;i<n;i++){
		if(i&&yz[i]==yz[i-1])continue;
		int at=0;
		for(int j=0;j<X[i].size();j++){
			int to=X[i][j];
			if(to-at>1){
				long long len=to-at;
				nega=(nega+len*(len-1)%mod*ot%mod*(ms-2))%mod;
			}
			if(to-at>2){
				long long len=to-at;
				ret=(ret+len*(len-1)%mod*(len-2)%mod*os%mod*2)%mod;
			}
			at=to+1;
		}
		if(at<a-1){
			long long len=a-at;
			nega=(nega+len*(len-1)%mod*ot%mod*(ms-2))%mod;
		}
		if(at<a-2){
			long long len=a-at;
			ret=(ret+len*(len-1)%mod*(len-2)%mod*os%mod*2)%mod;
		}
	}
	
	for(int i=0;i<n;i++){
		Y[i].push_back(b);
		if(i&&xz[i]==xz[i-1])continue;
		update(i,Y[i][0]-1);
	}
	int row=0;
	for(int i=0;i<n;i++){
		if(i&&yz[i]==yz[i-1])continue;
		long long hb=yz[i]-row;
		row=yz[i]+1;
		ret=(ret+hb*(a-1)%mod*((query(0,131071,0,n-1,1)%mod+(long long)(b-1)*(a-xs)%mod)%mod))%mod;
		int col=0;
		int xf=0;
		for(int j=0;j<X[i].size();j++){
			int xi=lower_bound(xz,xz+n,X[i][j])-xz;
			if(X[i][j]-col>=2){
				ret=(ret+(long long)(X[i][j]-col-1)*(query(0,131071,xf,xi-1,1)%mod+(long long)(b-1)*(X[i][j]-col-(sum[xi]-sum[xf]))%mod))%mod;
			}
			col=X[i][j]+1;
			xf=xi+1;
			next[xi]++;
			update(xi,Y[xi][next[xi]]-Y[xi][next[xi]-1]-2);
		}
		if(a-col>=2){
			ret=(ret+(long long)(a-col-1)*(query(0,131071,xf,n-1,1)%mod+(long long)(b-1)*(a-col-(sum[n]-sum[xf]))%mod))%mod;
		}
	}
	//printf("%lld %lld\n",ret,nega);
	ret=(ret+(long long)(b-row)*(a-1)%mod*(query(0,131071,0,n-1,1)%mod+(long long)(b-1)*(a-xs)%mod)%mod)%mod;
	ret=(ret+mod-nega)%mod;
	printf("%lld\n",ret);
}
accuracy: 1
time_ms: 200
memory_kb: 16688
final_rank: 1

code_snippet: #include<cstdio>
#include<map>
#include<vector>
#include<cassert>
#include<algorithm>

using namespace std;

const long long mod=1000000007;

map<int,vector<int> > sameX,sameY;

vector<int> xs,ys;

long long mult(long long x,long long y){
	return ((x%mod)*(y%mod))%mod;
}

long long ad(long long x,long long y){
	return (x+y)%mod;
}

int getId(vector<int> &vec,int val){
	return distance(vec.begin(),lower_bound(vec.begin(),vec.end(),val));
}

void print(vector<int> &vec){
	printf("{");
	for(int i=0;i<vec.size();i++){
		printf("%d ",vec[i]);
	}
	printf("}\n");
}

struct Segtree{
	static const int SZ=524288;
//	long long dat[2][SZ];
//	long long weight[SZ];
//	long long sum[2][SZ];
//	bool same[2][SZ];
//	long long res[SZ];
	int dat[2][SZ];
	int weight[SZ];
	int sum[2][SZ];
	bool same[2][SZ];
	int res[SZ];
	int N;
	void update(int l,int r,int t,int x,int k,int a,int b){
		if(r<=a||b<=l) return;
		if(l<=a&&b<=r){
			same[t][k]=true;
			dat[t][k]=x;
			sum[t][k]=mult(weight[k],x);
			res[k]=mult(sum[t^1][k],x);
			return;
		}
		for(int s=0;s<2;s++){
			if(same[s][k]){
				for(int j=k*2;j<=k*2+1;j++){
					same[s][j]=true;
					dat[s][j]=dat[s][k];
					sum[s][j]=mult(weight[j],dat[s][k]);
					res[j]=mult(sum[s^1][j],dat[s][j]);
				}
			}
		}
		same[t][k]=false;
		update(l,r,t,x,k*2,a,(a+b)/2);
		update(l,r,t,x,k*2+1,(a+b)/2,b);
		for(int s=0;s<2;s++){
			sum[s][k]=ad(sum[s][k*2],sum[s][k*2+1]);
		}
		res[k]=ad(res[k*2],res[k*2+1]);
	}
	void update(int l,int r,int t,int x){
//		printf("\n");
//		printf("update %d %d %d %d\n",l,r,t,x);
//		printf("before (%lld %lld %lld) (%lld %lld %lld) (%lld %lld %lld)\n",
//			sum[0][2],sum[1][2],res[2],sum[0][3],sum[1][3],res[3],sum[0][1],sum[1][1],res[1]);
		if(l>=r) return;
		update(l,r,t,x,1,0,N);
//		printf("cur_res=%lld\n",res[1]);
//		printf("updated (%lld %lld %lld) (%lld %lld %lld) (%lld %lld %lld)\n",
//			sum[0][2],sum[1][2],res[2],sum[0][3],sum[1][3],res[3],sum[0][1],sum[1][1],res[1]);
//		printf("\n");
	}
	void init(int *w,int N_){
		N=1;
		while(N<N_) N*=2;
		for(int i=N;i<=N*2-1;i++){
			weight[i]=w[i-N];
//			printf("%d",weight[i]);
		}
//		printf("\n");
		for(int i=N-1;i>=1;i--){
			weight[i]=weight[i*2]+weight[i*2+1];
		}
		same[0][1]=true;
		same[1][1]=true;
	}
	long long get(){
		return res[1];
	}
};

Segtree seg;

int rx[100100],ry[100100],K;
int X,Y;
long long num;

void compress(vector<int> &vec){
	sort(vec.begin(),vec.end());
	vec.erase(unique(vec.begin(),vec.end()),vec.end());
}

void precalc(){
	for(int i=0;i<K;i++){
		xs.push_back(rx[i]);
		ys.push_back(ry[i]);
		xs.push_back(rx[i]+1);
		ys.push_back(ry[i]+1);
		sameX[rx[i]].push_back(ry[i]);
		sameY[ry[i]].push_back(rx[i]);
	}
	xs.push_back(X);
	ys.push_back(Y);
	xs.push_back(0);
	ys.push_back(0);
	compress(xs);
	compress(ys);
	map<int,vector<int> > :: iterator it;
	for(it=sameX.begin();it!=sameX.end();it++){
		vector<int> &vec=it->second;
		sort(vec.begin(),vec.end());
	}
	for(it=sameY.begin();it!=sameY.end();it++){
		vector<int> &vec=it->second;
		sort(vec.begin(),vec.end());
	}
	num=mult(X,Y)-K;
	if(num<=0) num+=mod;
}

vector<int> ids;
void process1(vector<int> &vec){//yoko
//	printf("process1\n");
	if(vec.size()==0){
		seg.update(0,ys.size()-1,0,Y-1);
		return;
	}
	ids.clear();
	for(int i=0;i<vec.size();i++){
		int id=getId(ys,vec[i]);
		ids.push_back(id);
	}
	for(int i=0;i<ids.size();i++){
		int cur=ids[i];
		seg.update(cur,cur+1,0,0);
		if(i!=0){
			int prv=ids[i-1];
			int val=ys[cur]-ys[prv]-2;
			seg.update(prv+1,cur,0,val);
		}
	}
	if(ids[0]!=0){
		int val=ys[ids[0]]-1;
		seg.update(0,ids[0],0,val);
	}
	if(ys[ids.back()]<=Y-2){
		int val=Y-ys[ids.back()]-2;
		int id=ids.back();
		seg.update(id+1,ys.size()-1,0,val);
	}
}

int getRange(vector<int> &vec,int val){
//	assert(vec.size()>0);
	if(vec.size()==0) return X;
	int id=distance(vec.begin(),lower_bound(vec.begin(),vec.end(),val));
	if(id==vec.size()){
		return X-vec.back()-1;
	}
	if(id==0) return vec[0];
	if(vec[id]==0) return 0;
	return vec[id]-vec[id-1]-1;
}

void process2(vector<int> &vec,int cur_x){//freed tate
//	printf("process2\n");
	for(int i=0;i<vec.size();i++){
		int y=vec[i];
		if(y==Y) continue;
//		printf("vec[%d]=%d\n",i,vec[i]);
		int val;
		/*if(sameY.count(vec[i])>0&&sameY[vec[i]].back()<cur_x){
			val=X-sameY[vec[i]].back()-1;
		}else{
			val=getRange(sameY[vec[i]],cur_x,X);
		}*/
		val=getRange(sameY[vec[i]],cur_x);
//		print(sameY[vec[i]]);
		if(val!=0) val--;
		int id=getId(ys,vec[i]);
		seg.update(id,id+1,1,val);
	}
}

void process3(vector<int> &vec){//appeared tate
//	printf("process3\n");
	for(int i=0;i<vec.size();i++){
		int id=getId(ys,vec[i]);
		seg.update(id,id+1,1,0);
	}
}

int w[300300];
void segtree_init(){
	for(int i=0;i+1<ys.size();i++){
		w[i]=ys[i+1]-ys[i];
	}
	seg.init(w,(int)ys.size()-1);
}

void input(){
	scanf("%d%d%d",&X,&Y,&K);
	for(int i=0;i<K;i++){
		scanf("%d%d",rx+i,ry+i);
	}
}

long long solve1(){
	segtree_init();
	long long res=0;
	for(int i=0;i+1<xs.size();i++){
//		printf("xs[%d]=%d\n",i,xs[i]);
		process1(sameX[xs[i]]);
		if(i==0){
			process2(ys,xs[i]);
		}else{
			process2(sameX[xs[i-1]],xs[i]);
		}
		process3(sameX[xs[i]]);
		long long tmp=seg.get();
		long long cur=mult(tmp,xs[i+1]-xs[i]);
		res+=cur;
//		printf("tmp=%lld,w=%d\n",tmp,xs[i+1]-xs[i]);
	}
	return res;
}

long long C(long long N,long long K){
	if(N<K) return 0;
	long long num=1,den=1;
	for(int i=1;i<=K;i++){
		den*=i;
		num=mult(num,(N-i+1));
	}
	while(true){
		if(num%den==0) return num/den;
		num+=mod;
	}
}

vector<int> parse(vector<int> &vec,int all){
	vector<int> res;
	if(vec.size()==0) return res;
	int prv=-1;
	for(int i=0;i<vec.size();i++){
		res.push_back(vec[i]-prv-1);
		prv=vec[i];
	}
	res.push_back(all-vec.back()-1);
	return res;
}

long long solve2(){//a-b,c
	long long res=0;
	map<int,vector<int> > :: iterator it;
	
	//yoko
	int cnt=0;
	for(it=sameX.begin();it!=sameX.end();it++){
		vector<int> &ys=it->second;
		if(ys.size()==0) continue;
		vector<int> vec=parse(ys,Y);
		for(int i=0;i<vec.size();i++){
			long long tmp=C(vec[i],2);
			tmp=mult(tmp,num-vec[i]);
			res=ad(res,tmp);
//			res=ad(res,C(vec[i],3));
		}
		cnt++;
	}
	long long tmp=C(Y,2);
	tmp=mult(tmp,num-Y);
	tmp=mult(tmp,X-cnt);
	res=ad(res,tmp);
//	res=ad(res,mult(tmp,X-cnt));
	
	//tate
	cnt=0;
	for(it=sameY.begin();it!=sameY.end();it++){
		vector<int> &xs=it->second;
		if(xs.size()==0) continue;
		vector<int> vec=parse(xs,X);
		for(int i=0;i<vec.size();i++){
			long long tmp=C(vec[i],2);
			tmp=mult(tmp,num-vec[i]);
			res=ad(res,tmp);
//			res=ad(res,C(vec[i],3));
		}
		cnt++;
	}
	tmp=C(X,2);
	tmp=mult(tmp,num-X);
	tmp=mult(tmp,Y-cnt);
	res=ad(res,tmp);
	return res;
//	res=ad(res,mult(tmp,Y-cnt));
}

long long solve3(){//a-b-c
	long long res=0;
	map<int,vector<int> > :: iterator it;
	
	//yoko
	int cnt=0;
	for(it=sameX.begin();it!=sameX.end();it++){
		vector<int> &ys=it->second;
		if(ys.size()==0) continue;
		vector<int> vec=parse(ys,Y);
		for(int i=0;i<vec.size();i++){
			res=ad(res,C(vec[i],3));
//			printf("vec[i]=%d\n",vec[i]);
		}
		cnt++;
	}
	long long tmp=C(Y,3);
	res=ad(res,mult(tmp,X-cnt));
//	printf("yoko res=%lld\n",res);
	
	//tate
	cnt=0;
	for(it=sameY.begin();it!=sameY.end();it++){
		vector<int> &xs=it->second;
		if(xs.size()==0) continue;
		vector<int> vec=parse(xs,X);
		for(int i=0;i<vec.size();i++){
			res=ad(res,C(vec[i],3));
//			printf("vec=%d\n",vec[i]);
		}
		cnt++;
	}
	tmp=C(X,3);
	res=ad(res,mult(tmp,Y-cnt));
	return res;
}

long long solve4(){//all
	return C(num,3);
}

long long solve(){/*
	long long res=solve4();
	res+=solve1();
	res-=solve2();
	res-=solve3();*/
	long long val1=solve1();
	long long val2=solve2();
	long long val3=solve3();
	long long val4=solve4();
	long long res=val1-val2-val3+val4;
	res%=mod;
	if(res<0) res+=mod;
//	printf("%lld %lld %lld %lld\n",val1,val2,val3,val4);
	return res;
}

int main(){
	input();
	precalc();/*
	long long tmp=solve1();
	printf("tmp=%lld\n",tmp);*/
	long long res=solve();
	printf("%lld\n",res);
	return 0;
}
accuracy: 1
time_ms: 1140
memory_kb: 54524
final_rank: 8