description: Polycarp is very fond of playing the game Minesweeper. Recently he found a similar game and there are such rules.
  
  There are mines on the field, for each the coordinates of its location are known (x_i, y_i). Each mine has a lifetime in seconds, after which it will explode. After the explosion, the mine also detonates all mines vertically and horizontally at a distance of k (two perpendicular lines). As a result, we get an explosion on the field in the form of a "plus" symbol ('+'). Thus, one explosion can cause new explosions, and so on.
  
  Also, Polycarp can detonate anyone mine every second, starting from zero seconds. After that, a chain reaction of explosions also takes place. Mines explode instantly and also instantly detonate other mines according to the rules described above.
  
  Polycarp wants to set a new record and asks you to help him calculate in what minimum number of seconds all mines can be detonated.
  
  Input
  
  The first line of the input contains an integer t (1 ≤ t ≤ 10^4) — the number of test cases in the test.
  
  An empty line is written in front of each test suite.
  
  Next comes a line that contains integers n and k (1 ≤ n ≤ 2 ⋅ 10^5, 0 ≤ k ≤ 10^9) — the number of mines and the distance that hit by mines during the explosion, respectively.
  
  Then n lines follow, the i-th of which describes the x and y coordinates of the i-th mine and the time until its explosion (-10^9 ≤ x, y ≤ 10^9, 0 ≤ timer ≤ 10^9). It is guaranteed that all mines have different coordinates.
  
  It is guaranteed that the sum of the values n over all test cases in the test does not exceed 2 ⋅ 10^5.
  
  Output
  
  Print t lines, each of the lines must contain the answer to the corresponding set of input data — the minimum number of seconds it takes to explode all the mines.
  
  Example
  
  Input
  
  
  3
  
  5 0
  0 0 1
  0 1 4
  1 0 2
  1 1 3
  2 2 9
  
  5 2
  0 0 1
  0 1 4
  1 0 2
  1 1 3
  2 2 9
  
  6 1
  1 -1 3
  0 -1 9
  0 1 7
  -1 0 1
  -1 1 9
  -1 -1 7
  
  
  Output
  
  
  2
  1
  0
  
  Note
  
  <image> Picture from examples
  
  First example: 
  
    * 0 second: we explode a mine at the cell (2, 2), it does not detonate any other mine since k=0. 
    * 1 second: we explode the mine at the cell (0, 1), and the mine at the cell (0, 0) explodes itself. 
    * 2 second: we explode the mine at the cell (1, 1), and the mine at the cell (1, 0) explodes itself.
  
  
  
  Second example:
  
    * 0 second: we explode a mine at the cell (2, 2) we get: 
  
  <image>
  
    * 1 second: the mine at coordinate (0, 0) explodes and since k=2 the explosion detonates mines at the cells (0, 1) and (1, 0), and their explosions detonate the mine at the cell (1, 1) and there are no mines left on the field. 
solution: import java.io.*;
  import java.util.*;
  
  public class G {
      static HashMap<Bomb, ArrayList<Bomb>> adj;
  
      public static void main(String[] args) {
          FastIO io = new FastIO();
          int t = io.nextInt();
          while (t-- > 0) {
              int n = io.nextInt();
              int k = io.nextInt();
              adj = new HashMap<>();
              Bomb[] bombs = new Bomb[n];
              HashMap<Integer, ArrayList<Bomb>> rows = new HashMap<>();
              HashMap<Integer, ArrayList<Bomb>> cols = new HashMap<>();
              for (int i = 0; i < n; i++) {
                  Bomb bomb = new Bomb(io.nextInt(), io.nextInt(), io.nextInt(), i);
                  bombs[i] = bomb;
                  rows.putIfAbsent(bomb.x, new ArrayList<>());
                  rows.get(bomb.x).add(bomb);
                  cols.putIfAbsent(bomb.y, new ArrayList<>());
                  cols.get(bomb.y).add(bomb);
              }
              DisjointSets uf = new DisjointSets(n);
              Arrays.sort(bombs, (a, b) -> a.timer - b.timer);
              for (ArrayList<Bomb> br : rows.values()) {
                  br.sort((a, b) -> a.y - b.y);
                  for (int i = 1; i < br.size(); i++) {
                      if (br.get(i).y - br.get(i - 1).y <= k) {
                          uf.union(br.get(i - 1).ind, br.get(i).ind);
                      }
                  }
              }
              for (ArrayList<Bomb> br : cols.values()) {
                  br.sort((a, b) -> a.x - b.x);
                  for (int i = 1; i < br.size(); i++) {
                      if (br.get(i).x - br.get(i - 1).x <= k)
                          uf.union(br.get(i - 1).ind, br.get(i).ind);
                  }
              }
              int ans = uf.nrSCC - 1;
  
              for (int i = 0; i < n; i++) {
                  if (i > 0) {
                      uf.union(bombs[i].ind, bombs[i - 1].ind);
                  }
                  int aa = Math.max(bombs[i].timer, uf.nrSCC - 2);
                  ans = Math.min(aa, ans);
              }
              io.println(ans);
  
          }
          io.flush();
      }
  
      private static class DisjointSets {
          int[] parents;  // 0-indexed
          int[] sizes;
          int nrSCC;
  
          public DisjointSets(int size) {
              sizes = new int[size];
              parents = new int[size];
              nrSCC = size;
              Arrays.fill(sizes, 1);
              Arrays.fill(parents, -1);
          }
  
          // finds the "representative" node in a's component
          public int find(int x) {
              return parents[x] == -1 ? x : (parents[x] = find(parents[x]));
          }
  
          // returns whether the merge changed connectivity
          public void union(int x, int y) {
              int xRoot = find(x);
              int yRoot = find(y);
              if (xRoot == yRoot) {
                  return;
              }
              if (sizes[xRoot] < sizes[yRoot]) {
                  parents[xRoot] = yRoot;
                  sizes[yRoot] += sizes[xRoot];
              } else {
                  parents[yRoot] = xRoot;
                  sizes[xRoot] += sizes[yRoot];
              }
              nrSCC--;
          }
  
          // returns whether two nodes are in the same connected component
          public boolean connected(int x, int y) {
              return find(x) == find(y);
          }
  
  
      }
  
  
      private static class Bomb {
          int x, y, ind, timer;
  
          public Bomb(int x, int y, int timer, int ind) {
              this.timer = timer;
              this.x = x;
              this.y = y;
              this.ind = ind;
          }
  
          @Override
          public boolean equals(Object o) {
              if (this == o) return true;
              if (!(o instanceof Bomb)) return false;
              Bomb bomb = (Bomb) o;
              return x == bomb.x &&
                      y == bomb.y &&
                      timer == bomb.timer;
          }
  
          @Override
          public int hashCode() {
              return Objects.hash(x, y, timer);
          }
      }
  
  
      private static class FastIO extends PrintWriter {
          private InputStream stream;
          private byte[] buf = new byte[1 << 16];
          private int curChar, numChars;
  
          // standard input
          public FastIO() {
              this(System.in, System.out);
          }
  
          public FastIO(InputStream i, OutputStream o) {
              super(o);
              stream = i;
          }
  
          // file input
          public FastIO(String i, String o) throws IOException {
              super(new FileWriter(o));
              stream = new FileInputStream(i);
          }
  
          // throws InputMismatchException() if previously detected end of file
          private int nextByte() {
              if (numChars == -1) throw new InputMismatchException();
              if (curChar >= numChars) {
                  curChar = 0;
                  try {
                      numChars = stream.read(buf);
                  } catch (IOException e) {
                      throw new InputMismatchException();
                  }
                  if (numChars == -1) return -1; // end of file
              }
              return buf[curChar++];
          }
  
          // to read in entire lines, replace c <= ' '
          // with a function that checks whether c is a line break
          public String next() {
              int c;
              do {
                  c = nextByte();
              } while (c <= ' ');
              StringBuilder res = new StringBuilder();
              do {
                  res.appendCodePoint(c);
                  c = nextByte();
              } while (c > ' ');
              return res.toString();
          }
  
          public int nextInt() { // nextLong() would be implemented similarly
              int c;
              do {
                  c = nextByte();
              } while (c <= ' ');
              int sgn = 1;
              if (c == '-') {
                  sgn = -1;
                  c = nextByte();
              }
              int res = 0;
              do {
                  if (c < '0' || c > '9')
                      throw new InputMismatchException();
                  res = 10 * res + c - '0';
                  c = nextByte();
              } while (c > ' ');
              return res * sgn;
          }
  
          public double nextDouble() {
              return Double.parseDouble(next());
          }
      }
  
  }
  