Problem:
Score : 500 points

Problem Statement

We have a grid of squares with N rows and M columns. Let (i, j) denote the square at the i-th row from the top and j-th column from the left. We will choose K of the squares and put a piece on each of them.

If we place the K pieces on squares (x_1, y_1), (x_2, y_2), ..., and (x_K, y_K), the cost of this arrangement is computed as:

\sum_{i=1}^{K-1} \sum_{j=i+1}^K (|x_i - x_j| + |y_i - y_j|)

Find the sum of the costs of all possible arrangements of the pieces. Since this value can be tremendous, print it modulo 10^9+7.

We consider two arrangements of the pieces different if and only if there is a square that contains a piece in one of the arrangements but not in the other.

Constraints

2 \leq N \times M \leq 2 \times 10^5

2 \leq K \leq N \times M

All values in input are integers.

Input

Input is given from Standard Input in the following format:

N M K

Output

Print the sum of the costs of all possible arrangements of the pieces, modulo 10^9+7.

Sample Input 1

2 2 2

Sample Output 1

8

There are six possible arrangements of the pieces, as follows:

((1,1),(1,2)), with the cost |1-1|+|1-2| = 1

((1,1),(2,1)), with the cost |1-2|+|1-1| = 1

((1,1),(2,2)), with the cost |1-2|+|1-2| = 2

((1,2),(2,1)), with the cost |1-2|+|2-1| = 2

((1,2),(2,2)), with the cost |1-2|+|2-2| = 1

((2,1),(2,2)), with the cost |2-2|+|1-2| = 1

The sum of these costs is 8.

Sample Input 2

4 5 4

Sample Output 2

87210

Sample Input 3

100 100 5000

Sample Output 3

817260251

Be sure to print the sum modulo 10^9+7.

Solution in Rust (1.15.1) (judge verdict: Accepted)

macro_rules ! input { ( source = $ s : expr , $ ( $ r : tt ) * ) => { let mut iter = $ s . split_whitespace ( ) ; input_inner ! { iter , $ ( $ r ) * } } ; ( iter = $ iter : ident , $ ( $ r : tt ) * ) => { let s = { use std :: io :: Read ; let mut s = String :: new ( ) ; std :: io :: stdin ( ) . read_to_string ( & mut s ) . unwrap ( ) ; s } ; let mut $ iter = s . split_whitespace ( ) ; input_inner ! { $ iter , $ ( $ r ) * } } ; ( $ ( $ r : tt ) * ) => { let s = { use std :: io :: Read ; let mut s = String :: new ( ) ; std :: io :: stdin ( ) . read_to_string ( & mut s ) . unwrap ( ) ; s } ; let mut iter = s . split_whitespace ( ) ; input_inner ! { iter , $ ( $ r ) * } } ; }
macro_rules ! input_inner { ( $ iter : expr ) => { } ; ( $ iter : expr , ) => { } ; ( $ iter : expr , mut $ var : ident : $ t : tt $ ( $ r : tt ) * ) => { let mut $ var = read_value ! ( $ iter , $ t ) ; input_inner ! { $ iter $ ( $ r ) * } } ; ( $ iter : expr , mut $ var : ident $ ( $ r : tt ) * ) => { input_inner ! { $ iter , mut $ var : usize $ ( $ r ) * } } ; ( $ iter : expr , $ var : ident : $ t : tt $ ( $ r : tt ) * ) => { let $ var = read_value ! ( $ iter , $ t ) ; input_inner ! { $ iter $ ( $ r ) * } } ; ( $ iter : expr , $ var : ident $ ( $ r : tt ) * ) => { input_inner ! { $ iter , $ var : usize $ ( $ r ) * } } ; }
macro_rules ! read_value { ( $ iter : expr , ( $ ( $ t : tt ) ,* ) ) => { ( $ ( read_value ! ( $ iter , $ t ) ) ,* ) } ; ( $ iter : expr , [ $ t : tt ; $ len : expr ] ) => { ( 0 ..$ len ) . map ( | _ | read_value ! ( $ iter , $ t ) ) . collect ::< Vec < _ >> ( ) } ; ( $ iter : expr , { chars : $ base : expr } ) => { read_value ! ( $ iter , String ) . chars ( ) . map ( | c | ( c as u8 - $ base as u8 ) as usize ) . collect ::< Vec < usize >> ( ) } ; ( $ iter : expr , { char : $ base : expr } ) => { read_value ! ( $ iter , { chars : $ base } ) [ 0 ] } ; ( $ iter : expr , chars ) => { read_value ! ( $ iter , String ) . chars ( ) . collect ::< Vec < char >> ( ) } ; ( $ iter : expr , char ) => { read_value ! ( $ iter , chars ) [ 0 ] } ; ( $ iter : expr , usize1 ) => { read_value ! ( $ iter , usize ) - 1 } ; ( $ iter : expr , $ t : ty ) => { $ iter . next ( ) . unwrap ( ) . parse ::<$ t > ( ) . unwrap ( ) } ; }
pub mod at {
    use std;
    pub const MOD: i64 = 1_000_000_007;
    #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
    pub struct Modi64(pub i64);
    impl Modi64 {
        #[inline]
        pub fn new(i: i64) -> Modi64 {
            Modi64(Modi64::modulo(i, MOD))
        }
        #[inline]
        pub fn modulo(i: i64, m: i64) -> i64 {
            if i < 0 {
                i % m + m
            } else {
                i % m
            }
        }
        pub fn pow(self, y: i64) -> Modi64 {
            let mut y = Modi64(Modi64::modulo(y, MOD - 1));
            let mut x = 1.into();
            let mut base = self;
            while y.0 > 0 {
                if y.0 & 1 == 1 {
                    x *= base;
                }
                base *= base;
                y = Modi64(y.0 >> 1);
            }
            x
        }
        #[inline]
        pub fn inv(self) -> Modi64 {
            self.pow(MOD - 2)
        }
    }
    impl From<i64> for Modi64 {
        fn from(i: i64) -> Modi64 {
            Modi64::new(i)
        }
    }
    impl std::ops::Add for Modi64 {
        type Output = Modi64;
        fn add(self, rhs: Modi64) -> Modi64 {
            (self.0 + rhs.0).into()
        }
    }
    impl std::ops::AddAssign for Modi64 {
        fn add_assign(&mut self, rhs: Modi64) {
            *self = *self + rhs;
        }
    }
    impl std::ops::Sub for Modi64 {
        type Output = Modi64;
        fn sub(self, rhs: Modi64) -> Modi64 {
            (self.0 - rhs.0).into()
        }
    }
    impl std::ops::SubAssign for Modi64 {
        fn sub_assign(&mut self, rhs: Modi64) {
            *self = *self - rhs;
        }
    }
    impl std::ops::Mul for Modi64 {
        type Output = Modi64;
        fn mul(self, rhs: Modi64) -> Modi64 {
            (self.0 * rhs.0).into()
        }
    }
    impl std::ops::MulAssign for Modi64 {
        fn mul_assign(&mut self, rhs: Modi64) {
            *self = *self * rhs;
        }
    }
    impl std::ops::Div for Modi64 {
        type Output = Modi64;
        fn div(self, rhs: Modi64) -> Modi64 {
            self * rhs.inv()
        }
    }
    impl std::ops::DivAssign for Modi64 {
        fn div_assign(&mut self, rhs: Modi64) {
            *self = *self / rhs;
        }
    }
    impl std::ops::Neg for Modi64 {
        type Output = Modi64;
        fn neg(self) -> Modi64 {
            (-self.0).into()
        }
    }
    impl std::iter::Sum for Modi64 {
        fn sum<I: Iterator<Item = Modi64>>(iter: I) -> Modi64 {
            iter.fold(0.into(), std::ops::Add::add)
        }
    }
    impl<'a> std::iter::Sum<&'a Modi64> for Modi64 {
        fn sum<I: Iterator<Item = &'a Modi64>>(iter: I) -> Modi64 {
            iter.fold(0.into(), |x, &y| x + y)
        }
    }
    #[derive(Clone, Debug)]
    pub struct MemorizedFactorial {
        n: usize,
        fact: Vec<Modi64>,
        inv_fact: Vec<Modi64>,
    }
    impl MemorizedFactorial {
        pub fn new(max_n: usize) -> MemorizedFactorial {
            let mut fact = vec![1.into(); max_n + 1];
            let mut inv_fact = vec![1.into(); max_n + 1];
            for i in 1..(max_n + 1) {
                fact[i] = fact[i - 1] * (i as i64).into();
                inv_fact[i] = inv_fact[i - 1] / (i as i64).into();
            }
            MemorizedFactorial {
                n: max_n + 1,
                fact: fact,
                inv_fact: inv_fact,
            }
        }
        pub fn combination(&self, n: usize, r: usize) -> Modi64 {
            if r <= n {
                self.fact[n] * self.inv_fact[r] * self.inv_fact[n - r]
            } else {
                0.into()
            }
        }
        pub fn permutation(&self, n: usize, r: usize) -> Modi64 {
            if r <= n {
                self.fact[n] * self.inv_fact[n - r]
            } else {
                0.into()
            }
        }
        pub fn homogeneous_product(&self, n: usize, r: usize) -> Modi64 {
            if n != 0 && r != 0 {
                self.combination(n + r - 1, r)
            } else {
                1.into()
            }
        }
    }
}
use at::*;
fn main() {
    input! { n, m, k };
    let c = MemorizedFactorial::new(n * m);
    let mut ans = c.combination(n * m - 2, k - 2);
    let n = Modi64(n as i64);
    let m = Modi64(m as i64);
    let o = 1.into();
    ans *= n * m * ((m - o) * n * (m + o) + (n - o) * m * (n + o)) / 6.into();
    println!("{}", ans.0);
}