description: On an endless checkered sheet of paper, n cells are chosen and colored in three colors, where n is divisible by 3. It turns out that there are exactly n/3 marked cells of each of three colors! 
  
  Find the largest such k that it's possible to choose k/3 cells of each color, remove all other marked cells, and then select three rectangles with sides parallel to the grid lines so that the following conditions hold:
  
    * No two rectangles can intersect (but they can share a part of the boundary). In other words, the area of intersection of any two of these rectangles must be 0.
    * The i-th rectangle contains all the chosen cells of the i-th color and no chosen cells of other colors, for i = 1, 2, 3. 
  
  Input
  
  The first line of the input contains a single integer n — the number of the marked cells (3 ≤ n ≤ 10^5, n is divisible by 3).
  
  The i-th of the following n lines contains three integers x_i, y_i, c_i (|x_i|,|y_i| ≤ 10^9; 1 ≤ c_i ≤ 3), where (x_i, y_i) are the coordinates of the i-th marked cell and c_i is its color.
  
  It's guaranteed that all cells (x_i, y_i) in the input are distinct, and that there are exactly n/3 cells of each color.
  
  Output
  
  Output a single integer k — the largest number of cells you can leave.
  
  Examples
  
  Input
  
  
  9
  2 3 1
  4 1 2
  2 1 3
  3 4 1
  5 3 2
  4 4 3
  2 4 1
  5 2 2
  3 5 3
  
  
  Output
  
  
  6
  
  
  Input
  
  
  3
  1 1 1
  2 2 2
  3 3 3
  
  
  Output
  
  
  3
  
  Note
  
  In the first sample, it's possible to leave 6 cells with indexes 1, 5, 6, 7, 8, 9.
  
  In the second sample, it's possible to leave 3 cells with indexes 1, 2, 3.
solution: import java.io.BufferedOutputStream;
  import java.io.BufferedReader;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.InputStreamReader;
  import java.io.PrintWriter;
  import java.util.Arrays;
  import java.util.Comparator;
  import java.util.InputMismatchException;
  import java.util.StringTokenizer;
  
  import static java.lang.Math.max;
  import static java.lang.Math.min;
  
  
  /**
   __                  __
   ( _)                ( _)
   / / \\              / /\_\_
   / /   \\            / / | \ \
   / /     \\          / /  |\ \ \
   /  /   ,  \ ,       / /   /|  \ \
   /  /    |\_ /|      / /   / \   \_\
   /  /  |\/ _ '_| \   / /   /   \    \\
   |  /   |/  0 \0\    / |    |    \    \\
   |    |\|      \_\_ /  /    |     \    \\
   |  | |/    \.\ o\o)  /      \     |    \\
   \    |     /\\`v-v  /        |    |     \\
   | \/    /_| \\_|  /         |    | \    \\
   | |    /__/_ `-` /   _____  |    |  \    \\
   \|    [__]  \_/  |_________  \   |   \    ()
   /    [___] (    \         \  |\ |   |   //
   |    [___]                  |\| \|   /  |/
   /|    [____]                  \  |/\ / / ||
   (  \   [____ /     ) _\      \  \    \| | ||
   \  \  [_____|    / /     __/    \   / / //
   |   \ [_____/   / /        \    |   \/ //
   |   /  '----|   /=\____   _/    |   / //
   __ /  /        |  /   ___/  _/\    \  | ||
   (/-(/-\)       /   \  (/\/\)/  |    /  | /
   (/\/\)           /   /   //
   _________/   /    /
   \____________/    (
  
  
   @author KhanhNguyenn
   */
   
   /* Interesting problem, ideas from machine learning model SVM, using bondary to do  
   segmentation for plane to do classification. There will be 2 shape
   
   |a|b|c|   |   a    |
             ----------
             | b |  c |
   */
  
  public class s1608E {
  
      int[][] C = {
          {1, 2, 3},  
          {1, 3, 2},  
          {2, 1, 3},  
          {2, 3, 1},  
          {3, 1, 2},  
          {3, 2, 1},  
      };
      
      int n;
      int[] totalC = new int[4];
      int minTotalC = 0;
      void solve() {
          n = cint();
          P[] a = new P[n];
          for (int i = 0; i < n; i++) {
              a[i] = new P(cint(), cint(), (byte)cint());
              totalC[a[i].c]++;
          }
          //viz(a);
          minTotalC = min(min(totalC[1], totalC[2]), totalC[3]);
          
          int k = 0;
          k = max(k, solve(a));
          k = max(k, solve(rotate(a)));
          k = max(k, solve(rotate(a)));
          k = max(k, solve(rotate(a)));
          
          cout(k*3);
      }
      
      int solve(P[] a) {
          int k = 0;
          P[] b = Arrays.copyOf(a, n);
          Arrays.sort(a, Comparator.comparingInt((P o) -> o.x));
          Arrays.sort(b, Comparator.comparingInt((P o) -> o.y));
          for (int[] c : C) {
              k = max(k, solveHor(a, c));
              k = max(k, solveT(a, b, c));
          }
          return k;
      }
  
      int solveHor(P[] a, int[] c) {
          int[] m = new int[3];
          m[1] = totalC[c[1]];
          int k = 0;
          for (int i = 0, j = n-1; i <= j;) {
              if (m[0] < m[2]) {
                  do {
                      if (a[i].c == c[0]) {
                          m[0]++;
                      }
                      if (a[i].c == c[1]) {
                          m[1]--;
                      }
                      i++;
                  } while (i <= j && a[i-1].x == a[i].x);
              } else {
                  do {
                      if (a[j].c == c[2]) {
                          m[2]++;
                      }
                      if (a[j].c == c[1]) {
                          m[1]--;
                      }
                      j--;
                  } while (i <= j && a[j+1].x == a[j].x);
              }
              k = max(k, min(min(m[0], m[1]), m[2])); 
          }
          return k;
      }
  
      int solveT(P[] a, P[] b, int[] c) {
          int low = 0;
          int high = minTotalC;
          while (low <= high) {
              int mid = (low + high) / 2;
              boolean midVal = checkSolveT(a, b, c, mid);
  
              if (midVal) {
                  low = mid + 1;
              } else {
                  high = mid - 1;
              }
          }
          return low - 1;
      }
      
      boolean checkSolveT(P[] a, P[] b, int[] c, int k) {
          int t0 = 0, x0 = 0;
          for (int i = 0; i < n && t0 < k; i++) {
              if (a[i].c == c[0]) {
                  t0++;
                  x0 = a[i].x;
              }
          }
          if (t0 < k) {
              return false;
          }
          int t1 = 0, t2 = 0;
          for (int i = 0, j = n-1; i <= j;) {
              if (t1 < t2) {
                  do {
                      if (b[i].c == c[1] && b[i].x > x0) {
                          t1++;
                      }
                      i++;
                  } while (i <= j && b[i-1].y == b[i].y);
              } else {
                  do {
                      if (b[j].c == c[2] && b[j].x > x0) {
                          t2++;
                      }
                      j--;
                  } while (i <= j && b[j+1].y == b[j].y);
              }
          }
          return t1 >= k && t2 >= k;
      }
      
      P[] rotate(P[] a) {
          for (int i = 0; i < n; i++) {
              a[i] = new P(a[i].y, -a[i].x, a[i].c);
          }
          return a;
      }
      
      class P {
          final int x;
          final int y;
          final byte c;
  
          public P(int x, int y, byte c) {
              this.x = x;
              this.y = y;
              this.c = c;
          }
      }
  
      public static void main(String... args) {
  //        int t = in.nextInt();
  //        for (int test = 0; test < t; test++) {
              new s1608E().solve();
  //        }
          out.flush();
      }
  
      static final QuickReader in = new QuickReader(System.in);
      static final PrintWriter out = new PrintWriter(new BufferedOutputStream(System.out, 131_072));
  
      static int cint() {
          return in.nextInt();
      }
  
      static long clong() {
          return in.nextLong();
      }
  
      static String cstr() {
          return in.nextLine();
      }
  
      static int[] carr_int(int n) {
          return in.nextInts(n);
      }
  
      static long[] carr_long(int n) {
          return in.nextLongs(n);
      }
  
      static void cout(int... a) {
          StringBuilder buf = new StringBuilder();
          for (int i = 0; i < a.length; i++) {
              if (i != 0) {
                  buf.append(' ');
              }
              buf.append(a[i]);
          }
          out.println(buf);
      }
  
      static void cout(long... a) {
          StringBuilder buf = new StringBuilder();
          for (int i = 0; i < a.length; i++) {
              if (i != 0) {
                  buf.append(' ');
              }
              buf.append(a[i]);
          }
          out.println(buf);
      }
  
      static void cout(Object o) {
          out.println(o);
      }
  
      static class QuickReader {
          BufferedReader in;
          StringTokenizer token;
  
          public QuickReader(InputStream ins) {
              in = new BufferedReader(new InputStreamReader(ins));
              token = new StringTokenizer("");
          }
  
          public boolean hasNext() {
              while (!token.hasMoreTokens()) {
                  try {
                      String s = in.readLine();
                      if (s == null) {
                          return false;
                      }
                      token = new StringTokenizer(s);
                  } catch (IOException e) {
                      throw new InputMismatchException();
                  }
              }
              return true;
          }
  
          public String next() {
              hasNext();
              return token.nextToken();
          }
  
          public String nextLine() {
              try {
                  String s = in.readLine();
                  token = new StringTokenizer("");
                  return s;
              } catch (IOException e) {
                  throw new InputMismatchException();
              }
          }
  
          public int nextInt() {
              return Integer.parseInt(next());
          }
  
          public int[] nextInts(int n) {
              int[] res = new int[n];
              for (int i = 0; i < n; i++) {
                  res[i] = nextInt();
              }
              return res;
          }
  
          public long nextLong() {
              return Long.parseLong(next());
          }
  
          public long[] nextLongs(int n) {
              long[] res = new long[n];
              for (int i = 0; i < n; i++) {
                  res[i] = nextLong();
              }
              return res;
          }
      }
  }
  