#![allow(unused_imports)]
#![allow(unused_macros)]
use itertools::iproduct;
use std::cmp::{max, min};
use std::collections::*;
use std::io::{stdin, Read};
use std::ops::Range;

#[allow(unused_macros)]
macro_rules! parse {
    ($it: ident ) => {};
    ($it: ident, ) => {};
    ($it: ident, $var:ident : $t:tt $($r:tt)*) => {
        let $var = parse_val!($it, $t);
        parse!($it $($r)*);
    };
    ($it: ident, mut $var:ident : $t:tt $($r:tt)*) => {
        let mut $var = parse_val!($it, $t);
        parse!($it $($r)*);
    };
}

#[allow(unused_macros)]
macro_rules! parse_val {
    ($it: ident, [$t:tt; $len:expr]) => {
        (0..$len).map(|_| parse_val!($it, $t)).collect::<Vec<_>>();
    };
    ($it: ident, ($($t: tt),*)) => {
        ($(parse_val!($it, $t)),*)
    };
    ($it: ident, u1) => {
        $it.next().unwrap().parse::<usize>().unwrap() -1
    };
    ($it: ident, $t: ty) => {
        $it.next().unwrap().parse::<$t>().unwrap()
    };
}

#[cfg(debug_assertions)]
macro_rules! debug {
    ($( $args:expr ),*) => { eprintln!( $( $args ),* ); }
}

#[cfg(not(debug_assertions))]
macro_rules! debug {
    ($( $args:expr ),*) => {
        ()
    };
}

#[derive(Debug)]
struct Node {
    p: (usize, usize),
    left: Box<Option<Node>>,
    right: Box<Option<Node>>,
}

impl Node {
    fn count_brach(
        node: &Box<Option<Node>>,
        x: &Range<usize>,
        y: &Range<usize>,
        depth: usize,
    ) -> usize {
        match &**node {
            None => 0,
            Some(node) => node.count(x, y, depth + 1),
        }
    }

    fn count(&self, x: &Range<usize>, y: &Range<usize>, depth: usize) -> usize {
        if x.start >= x.end || y.start >= y.end {
            return 0;
        }
        let (p, r) = if depth % 2 == 0 {
            (self.p.0, x)
        } else {
            (self.p.1, y)
        };
        let mut ret = 0;
        if p > r.start {
            ret += Node::count_brach(&self.left, x, y, depth)
        }
        if p < r.end {
            ret += Node::count_brach(&self.right, x, y, depth)
        }
        if x.contains(&self.p.0) && y.contains(&self.p.1) {
            ret += 1;
        }
        ret
    }
}

fn make_kd(pl: &mut [(usize, usize)], depth: usize) -> Option<Node> {
    if pl.len() == 0 {
        return None;
    }
    if depth % 2 == 0 {
        pl.sort_by_key(|x| x.0)
    } else {
        pl.sort_by_key(|x| x.1)
    }
    let mid = pl.len() / 2;
    let (l, r) = pl.split_at_mut(mid);
    let (x, r) = r.split_at_mut(1);
    Some(Node {
        p: x[0],
        left: Box::new(make_kd(l, depth + 1)),
        right: Box::new(make_kd(r, depth + 1)),
    })
}

fn solve(s: &str) {
    let mut it = s.split_whitespace();
    parse!(it, n: usize, k: usize);
    let d = 4 * k;
    let mut s = vec![vec![0i64; d + 1]; d + 1];
    let k2 = 2 * k;
    for _ in 0..n {
        parse!(it, x: usize, y: usize, c: char);
        let x = x % k2;
        let y = (y + if c == 'W' { k } else { 0 }) % k2;
        s[x][y] += 1;
        s[x][y + k] -= 1;
        s[x + k][y] -= 1;
        s[x + k][y + k] += 1;

        let x = x + k;
        let y = y + k;

        s[x][y] += 1;
        s[x][y + k] -= 1;
        s[x + k][y] -= 1;
        s[x + k][y + k] += 1;
    }

    for i in 0..d {
        for j in 0..d {
            s[i][j + 1] += s[i][j]
        }
    }
    for j in 0..d {
        for i in 0..d {
            s[i + 1][j] += s[i][j]
        }
    }

    let mut t = vec![vec![0i64; 2 * k]; 2 * k];
    for i in 0..d {
        for j in 0..d {
            t[i % k2][j % k2] += s[i][j];
        }
    }

    let ret = *t.iter().map(|x| x.iter().max().unwrap()).max().unwrap();
    println!("{}", ret);
}

fn main() {
    let mut s = String::new();
    stdin().read_to_string(&mut s).unwrap();
    solve(&s);
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_input() {
        let s = "
        ";
        solve(s);
    }
}


Measured memory_bytes: 160364