Problem:
Score : 300 points

Problem Statement

You are given N real values A_1, A_2, \ldots, A_N.
Compute the number of pairs of indices (i, j)
such that i < j and the product A_i \cdot A_j is integer.

Constraints

2 \leq N \leq 200\,000

0 < A_i < 10^4

A_i is given with at most 9 digits after the decimal.

Input

Input is given from Standard Input in the following format.

N
A_1
A_2
\vdots
A_N

Output

Print the number of pairs with integer product A_i \cdot A_j (and i < j).

Sample Input 1

5
7.5
2.4
17.000000001
17
16.000000000

Sample Output 1

3

There are 3 pairs with integer product:

7.5 \cdot 2.4 = 18

7.5 \cdot 16 = 120

17 \cdot 16 = 272

Sample Input 2

11
0.9
1
1
1.25
2.30000
5
70
0.000000001
9999.999999999
0.999999999
1.000000001

Sample Output 2

8

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

use std::cmp::*;
use input_mcr::*;

use cumulative2::*;

fn main() {
    input! {
        n: usize,
        ss: [String; n],
    }
    let mut xs = vec![];
    for s in &ss {
        let ts: Vec<_> = s.split('.').collect();
        let a = ts[0].parse::<i64>().unwrap();
        let b = if ts.len() > 1 { 
            ts[1].parse::<i64>().unwrap() * 10i64.pow((9-ts[1].len()) as u32)
        } else { 0 };
        let mut x = a * 1_000_000_000 + b;
        let mut c2 = 0;
        let mut c5 = 0;
        while x % 2 == 0 {
            x /= 2;
            c2 += 1;
        }
        while x % 5 == 0 {
            x /= 5;
            c5 += 1;
        }
        let c2 = min(c2, 18);
        let c5 = min(c5, 18);
        xs.push((c2,c5));
    }
    //eprintln!("xs = {:?}", xs);
    let mut yss = vec![vec![0; 30]; 30];
    for &(c2,c5) in &xs {
        yss[18-c2][18-c5] += 1;
    }
    let yss_cu = gen_cu2(0, &yss);
    //eprintln!("yss_cu = {:?}", yss_cu);
    let mut res = 0i64;
    for &(c2, c5) in &xs {
        res += yss_cu[c2+1][c5+1];
        if c2 * 2 >= 18 && c5 * 2 >= 18 {
            res -= 1;
        }
    }
    let res = res / 2;
    println!("{}", res);
}
pub mod input_mcr {
    // ref: tanakh <https://qiita.com/tanakh/items/0ba42c7ca36cd29d0ac8>
    #[macro_export(local_inner_macros)]
    macro_rules! input {
        (source = $s:expr, $($r:tt)*) => {
            let mut parser = Parser::from_str($s);
            input_inner!{parser, $($r)*}
        };
        (parser = $parser:ident, $($r:tt)*) => {
            input_inner!{$parser, $($r)*}
        };
        (new_stdin_parser = $parser:ident, $($r:tt)*) => {
            let stdin = std::io::stdin();
            let reader = std::io::BufReader::new(stdin.lock());
            let mut $parser = Parser::new(reader);
            input_inner!{$parser, $($r)*}
        };
        ($($r:tt)*) => {
            input!{new_stdin_parser = parser, $($r)*}
        };
    }
    
    #[macro_export(local_inner_macros)]
    macro_rules! input_inner {
        ($parser:ident) => {};
        ($parser:ident, ) => {};
        ($parser:ident, $var:ident : $t:tt $($r:tt)*) => {
            let $var = read_value!($parser, $t);
            input_inner!{$parser $($r)*}
        };
    }
    
    #[macro_export(local_inner_macros)]
    macro_rules! read_value {
        ($parser:ident, ( $($t:tt),* )) => {
            ( $(read_value!($parser, $t)),* )
        };
        ($parser:ident, [ $t:tt ; $len:expr ]) => {
            (0..$len).map(|_| read_value!($parser, $t)).collect::<Vec<_>>()
        };
        ($parser:ident, chars) => {
            read_value!($parser, String).chars().collect::<Vec<char>>()
        };
        ($parser:ident, char_) => {
            read_value!($parser, String).chars().collect::<Vec<char>>()[0]
        };
        ($parser:ident, usize1) => {
            read_value!($parser, usize) - 1
        };
        ($parser:ident, line) => {
            $parser.next_line()
        };
        ($parser:ident, line_) => {
            $parser.next_line().chars().collect::<Vec<char>>()
        };
        ($parser:ident, $t:ty) => {
            $parser.next::<$t>().expect("Parse error")
        };
    }
    
    use std::io;
    use std::io::BufRead;
    use std::str;
    use std::collections::VecDeque;
    
    pub struct Parser<R> {
        pub reader: R,
        buf: VecDeque<u8>,
        parse_buf: Vec<u8>,
    }
    
    impl Parser<io::Empty> {
        pub fn from_str(s: &str) -> Parser<io::Empty> {
            Parser {
                reader: io::empty(),
                buf: VecDeque::from(s.as_bytes().to_vec()),
                parse_buf: vec![],
            }
        }
    }
    
    impl<R: BufRead> Parser<R> {
        pub fn new(reader: R) -> Parser<R> {
            Parser {
                reader: reader,
                buf: VecDeque::new(),
                parse_buf: vec![],
            }
        }
        pub fn update_buf(&mut self) {
            loop {
                let (len, complete) = {
                    let buf2 = self.reader.fill_buf().unwrap();
                    self.buf.extend(buf2.iter());
                    let len = buf2.len();
                    (len, buf2.last() < Some(&0x20))
                };
                self.reader.consume(len);
                if complete {
                    break;
                }
            }
        }
        pub fn next<T: str::FromStr>(&mut self) -> Result<T, T::Err> {
            loop {
                while let Some(c) = self.buf.pop_front() {
                    if c > 0x20 {
                        self.buf.push_front(c);
                        break;
                    }
                }
                self.parse_buf.clear();
                while let Some(c) = self.buf.pop_front() {
                    if c <= 0x20 {
                        self.buf.push_front(c);
                        break;
                    } else {
                        self.parse_buf.push(c);
                    }
                }
                if self.parse_buf.is_empty() {
                    self.update_buf();
                } else {
                    return unsafe { str::from_utf8_unchecked(&self.parse_buf) }.parse::<T>();
                }
            }
        }
        pub fn next_line(&mut self) -> String {
            loop {
                while let Some(c) = self.buf.pop_front() {
                    if c >= 0x20 {
                        self.buf.push_front(c);
                        break;
                    }
                }
                self.parse_buf.clear();
                while let Some(c) = self.buf.pop_front() {
                    if c < 0x20 {
                        self.buf.push_front(c);
                        break;
                    } else {
                        self.parse_buf.push(c);
                    }
                }
                if self.parse_buf.is_empty() {
                    self.update_buf();
                } else {
                    return unsafe { str::from_utf8_unchecked(&self.parse_buf) }.to_string();
                }
            }
        }
    }
}
pub mod cumulative2 {
    use std::ops::*;
    
    pub fn gen_cu2<T: Add<Output = T> + Sub<Output = T> + Copy>(
        zero: T,
        xss: &Vec<Vec<T>>,
    ) -> Vec<Vec<T>> {
        let mut res = vec![vec![zero; xss[0].len() + 1]; xss.len() + 1];
        for (i, ref xs) in xss.iter().enumerate() {
            for (k, &x) in xs.iter().enumerate() {
                res[i + 1][k + 1] = res[i + 1][k] + res[i][k + 1] - res[i][k] + x;
            }
        }
        res
    }
    
    pub fn range2<T: Add<Output = T> + Sub<Output = T> + Copy>(
        cu2: &Vec<Vec<T>>,
        l: usize,
        d: usize,
        r: usize,
        u: usize,
    ) -> T {
        cu2[u][r] + cu2[d][l] - cu2[u][l] - cu2[d][r]
    }
}