Problem:
Score : 700 points

Problem Statement

There are some coins in the xy-plane.
The positions of the coins are represented by a grid of characters with H rows and W columns.
If the character at the i-th row and j-th column, s_{ij}, is #, there is one coin at point (i,j); if that character is ., there is no coin at point (i,j). There are no other coins in the xy-plane.

There is no coin at point (x,y) where 1\leq i\leq H,1\leq j\leq W does not hold.
There is also no coin at point (x,y) where x or y (or both) is not an integer.
Additionally, two or more coins never exist at the same point.

Find the number of triples of different coins that satisfy the following condition:

Choosing any two of the three coins would result in the same Manhattan distance between the points where they exist.

Here, the Manhattan distance between points (x,y) and (x',y') is |x-x'|+|y-y'|.
Two triples are considered the same if the only difference between them is the order of the coins.

Constraints

1 \leq H,W \leq 300

s_{ij} is # or ..

Input

Input is given from Standard Input in the following format:

H W
s_{11}...s_{1W}
:
s_{H1}...s_{HW}

Output

Print the number of triples that satisfy the condition.

Sample Input 1

5 4
#.##
.##.
#...
..##
...#

Sample Output 1

3

((1,1),(1,3),(2,2)),((1,1),(2,2),(3,1)) and ((1,3),(3,1),(4,4)) satisfy the condition.

Sample Input 2

13 27
......#.........#.......#..
#############...#.....###..
..............#####...##...
...#######......#...#######
...#.....#.....###...#...#.
...#######....#.#.#.#.###.#
..............#.#.#...#.#..
#############.#.#.#...###..
#...........#...#...#######
#..#######..#...#...#.....#
#..#.....#..#...#...#.###.#
#..#######..#...#...#.#.#.#
#..........##...#...#.#####

Sample Output 2

870

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

fn read<T: std::str::FromStr>() -> T {
    let mut s = String::new();
    std::io::stdin().read_line(&mut s).ok();
    s.trim().parse().ok().unwrap()
}

fn read_vec<T: std::str::FromStr>() -> Vec<T> {
    read::<String>()
        .split_whitespace()
        .map(|e| e.parse().ok().unwrap())
        .collect()
}

fn manhattan_dist(x1: i32, y1: i32, x2: i32, y2: i32) -> usize {
    ((x1 - x2).abs() + (y1 - y2).abs()) as usize
}

fn main() {
    let v: Vec<usize> = read_vec();
    let (h, w) = (v[0], v[1]);
    let mut grid: Vec<Vec<bool>> = Vec::new();
    for _ in 0..h {
        let s: String = read();
        let mut line: Vec<bool> = Vec::new();
        for ch in s.chars() {
            if ch == '#' {
                line.push(true);
            } else {
                line.push(false);
            }
        }
        grid.push(line);
    }

    let mut rt_to_lb_group = Vec::new();
    let mut rt_to_lb_accum_sum = Vec::new();
    for i in 0..h + w - 1 {
        let mut x = 0;
        let mut y = i as i32;
        let mut line = Vec::new();
        let mut sums = vec![0];
        while y >= 0 && x < w as i32 {
            if x >= 0 && x < w as i32 && y >= 0 && y < h as i32 && grid[y as usize][x as usize] {
                line.push((x, y));
                let cur_sum = sums.last().unwrap() + 1;
                sums.push(cur_sum);
            } else {
                let cur_sum = sums.last().unwrap() + 0;
                sums.push(cur_sum);
            }
            x += 1;
            y -= 1;
        }
        rt_to_lb_group.push(line);
        rt_to_lb_accum_sum.push(sums);
    }

    // println!("{:?}", rt_to_lb_group);
    // println!("{:?}", rt_to_lb_accum_sum);

    let mut rb_to_lt_group = Vec::new();
    let mut rb_to_lt_accum_sum = Vec::new();
    for i in 0..h + w - 1 {
        let mut x = 0;
        let mut y = h as i32 - i as i32 - 1;
        let mut line = Vec::new();
        let mut sums = vec![0];
        while x >= 0 && y < h as i32 {
            if x >= 0 && x < w as i32 && y >= 0 && y < h as i32 && grid[y as usize][x as usize] {
                line.push((x, y));
                let cur_sum = sums.last().unwrap() + 1;
                sums.push(cur_sum);
            } else {
                let cur_sum = sums.last().unwrap() + 0;
                sums.push(cur_sum);
            }
            x += 1;
            y += 1;
        }
        rb_to_lt_accum_sum.push(sums);
        rb_to_lt_group.push(line);
    }

    // println!("{:?}", rb_to_lt_group);
    // println!("{:?}", rb_to_lt_accum_sum);

    let mut ans = 0;

    for (line_index, line) in rt_to_lb_group.iter().by_ref().enumerate() {
        if line.len() < 2 {
            continue;
        }
        for i in 0..line.len() - 1 {
            for j in i + 1..line.len() {
                let dist = manhattan_dist(line[i].0, line[i].1, line[j].0, line[j].1);
                if line_index >= dist {
                    let target_accum = &rt_to_lb_accum_sum[line_index - dist];
                    let begin = std::cmp::max(0, line[i].0 - dist as i32 / 2) as usize;
                    let end = std::cmp::min(target_accum.len() as i32 - 1, line[j].0 - dist as i32 / 2 + 1) as usize;
                    ans += target_accum[end] - target_accum[begin];
                    // println!("1 s ans {0} {1} {2} {3} {4}", ans, line[i].0, line[i].1, line[j].0, line[j].1);
                    // println!("line {2} begin {0}({4}) end {1}({5}) len {3}", begin, end, line_index - dist, target_accum.len(), target_accum[begin], target_accum[end]);

                }
                if line_index + dist < rt_to_lb_group.len() {
                    let target_accum = &rt_to_lb_accum_sum[line_index + dist];
                    let begin = std::cmp::max(0, line[i].0 + dist as i32 / 2) as usize;
                    let end = std::cmp::min(target_accum.len() as i32 - 1, line[j].0 + dist as i32 / 2 + 1) as usize;
                    ans += target_accum[end] - target_accum[begin];
                    // println!("2 l ans {0} {1} {2} {3} {4}", ans, line[i].0, line[i].1, line[j].0, line[j].1);
                    // println!("line {2} begin {0}({4}) end {1}({5}) len {3}", begin, end, line_index + dist, target_accum.len(), target_accum[begin], target_accum[end]);
                }
            }
        }
    }
    // println!("{:?}", ans);

    for (line_index, line) in rb_to_lt_group.iter().by_ref().enumerate() {
        if line.len() < 2 {
            continue;
        }
        for i in 0..line.len() - 1 {
            for j in i + 1..line.len() {
                let dist = manhattan_dist(line[i].0, line[i].1, line[j].0, line[j].1);
                if line_index >= dist {
                    let target_accum = &rb_to_lt_accum_sum[line_index - dist];
                    let begin = std::cmp::max(0, line[i].0 - dist as i32 / 2 + 1) as usize;
                    let end = std::cmp::min(target_accum.len() as i32 - 1, std::cmp::max(line[j].0 - dist as i32 / 2, 0)) as usize;
                    ans += target_accum[end] - target_accum[begin];
                    // println!("3 s ans {0} {1} {2} {3} {4}", ans, line[i].0, line[i].1, line[j].0, line[j].1);
                    // println!("3 line {2} begin {0} end {1}", begin, end, line_index - dist);
                }
                if line_index + dist < rb_to_lt_group.len() {
                    let target_accum = &rb_to_lt_accum_sum[line_index + dist];
                    let begin = std::cmp::max(0, line[i].0 + dist as i32 / 2 + 1) as usize;
                    let end = std::cmp::min(target_accum.len() as i32 - 1, std::cmp::max(line[j].0 + dist as i32 / 2, 0)) as usize;
                    ans += target_accum[end] - target_accum[begin];
                    // println!("4 l ans {0} {1} {2} {3} {4}", ans, line[i].0, line[i].1, line[j].0, line[j].1);
                    // println!("4 line {2} begin {0} end {1}", begin, end, line_index + dist);
                }
            }
        }
    }
    println!("{:?}", ans);
}