#include <iostream>
#include <fstream>
#include <cmath>
#include <string>
#include <vector>
#include <map>
#include <queue>
#include <set>
#include <unordered_set>
#include <bitset>

typedef long long ll;
using std::vector;
using std::string;
template<typename T> void ndarray(vector<T> &vec, int len) { vec.resize(len); }
template<typename T, typename... Args> void ndarray(vector<T> &vec, int len, Args... args) {
    vec.resize(len);
    for (auto &v : vec) ndarray(v, args...);
}

//////////////////////////////////////

template<typename T>
struct CumSum2D {
    int xnum, ynum;
    vector<vector<T>> data;
    vector<vector<T>> cumsum;
    
    CumSum2D(int xnum, int ynum) : xnum(xnum), ynum(ynum) {
        ndarray(data, xnum+1, ynum+1);
        ndarray(cumsum, xnum+1, ynum+1);
    }
    
    int add(int x, int y, T value) {
        //x,y 0~_num-1
        ++x;
        ++y;
        if (x > xnum || y > ynum) return -1;
        data[x][y] += value;
        return 0;
    }
    
    int add_imos(int startx, int starty, int endx, int endy) {
        //start_ 0~_num-1
        //end_ 1~_num
        if (add(endx, endy, (T) 1) == 1) return -1;
        if (add(startx, endy, (T) 1) == -1) return -1;
        if (add(endx, starty, (T) 1) == -1) return -1;
        if (add(startx, starty, (T) 1) == 1) return -1;
        return 0;
    }
    
    void build() {
        for (int i = 0; i < xnum; i++) {
            for (int j = 0; j < ynum; j++) {
                cumsum[i+1][j+1] = data[i+1][j+1] + cumsum[i+1][j] + cumsum[i][j+1] - cumsum[i][j];
            }
        }
    }
    
    T get(int startx, int starty, int endx, int endy) {
        //[(startx, starty) ~ (endx, endy))のdataの和を返す
        //start_ 0~_num-1
        //end_ 1~_num
        return (cumsum[endx][endy] - cumsum[endx][starty] - cumsum[startx][endy] + cumsum[startx][starty]);
    }
    
    void print() {
        std::cout << "cumsum" << std::endl;
        for (auto vec : cumsum) {
            for (auto val : vec) {
                std::cout << val << " ";
            }
            std::cout << std::endl;
        }
    }
    void print_data() {
        std::cout << "data" << std::endl;
        for (auto vec : data) {
            for (auto val : vec) {
                std::cout << val << " ";
            }
            std::cout << std::endl;
        }
    }
};

//////////////////////////////////////

int main() {
    //std::ifstream in("0_normal_2");
    //std::cin.rdbuf(in.rdbuf());
    
    int N, K;
    std::cin >> N >> K;
    int K2 = K * 2;
    CumSum2D<int> cs(K2, K2);
    
    //Wは+KでBに変換可能
    //mod 2*Kを取って良い
    
    //1.叶えられる希望の最大値 = 必要な塗り方の重なりの最大値：imos法
    //塗り方は黒の塊の左下位置Bを代表にとる
    //一つの希望に対し、黒は最大5つの範囲が存在するが、Bは2つの範囲のいずれかに属する
    //これを場合分けして各範囲で足す手もあるが、大変
    
    //2.Bを決め、その場合に含まれる希望の数を累積和で数える
    //場合分けの代わりに、もう一つの範囲分にもaddしておく
    //Bは(0,0)~(K-1,K-1)のみ考えれば十分
    //この方が場合分けが少なくて楽
    
    for (int i = 0; i < N; i++) {
        int x, y;
        std::cin >> x >> y;
        char c;
        std::cin >> c;
        if (c == 'W') x += K;
        x = x % K2;
        y = y % K2;
        if(y >= K) {
            y -= K;
            x = (x + K) % K2;
        }
        if (cs.add(x, y, 1) == -1) return -1;
        if (cs.add((x+K)%K2, y+K, 1) == -1) return -1;
    }
    cs.build();
    //cs.print_data();
    //cs.print();
    
    int ans = 0;
    for (int i = 0; i < K; i++) {
        for (int j = 0; j < K; j++) {
            auto b = cs.get(i, j, i+K, j+K);
            auto res = std::max(b, N-b);
            ans = std::max(ans, res);
        }
    }
    std::cout << ans << std::endl;
    
    return 0;
}



Measured memory_bytes: 31616