import java.util.*;


public class Solution {
    /**
     * max increasing subsequence
     * @param arr int整型一维数组 the array
     * @return int整型
     */
    public int MLS (int[] arr) {
        // disjoint set
        DisjointSet dSet = new DisjointSet(arr);

        for (Integer node : dSet.nodes) {
            dSet.union(node, node - 1); // 不断地寻找有无连续的节点
        }

        return dSet.maxSize;
    }
}

class DisjointSet {

    HashSet<Integer> nodes = new HashSet<>(); // 记录所有的节点
    HashMap<Integer, Integer> parent = new HashMap<>(); // 记录所有节点的直接父节点
    HashMap<Integer, Integer> size = new HashMap<>(); // 记录所有以某个节点为根的树的大小
    int maxSize = 1;

    public DisjointSet(int[] arr) {
        // 按照并查集的特性,一开始所有节点的父节点指针都指向自己(这里使用HashMap来模拟两个节点之间的指向关系)
        for (int i = 0; i < arr.length; i++) {
            parent.put(arr[i], arr[i]);
            size.put(arr[i], 1); // 初始大小为1
            nodes.add(arr[i]);
        }
    }

    public void union(int node1, int node2) {

        if (!nodes.contains(node2)) { // 有了这一步让我想了想 还不如直接用现成的集合做,因为这一步导致并没有快多少,这个代码主要写来理解一下并查集这个数据结构吧
            return;
        }

        int root1 = find(node1); //寻找父节点
        int root2 = find(node2);

        //合并两个树,大树的根作为小树根的父节点以实现合并 
        if (size.get(root1) > size.get(root2)) {
            parent.put(root2, root1);
            size.put(root1, size.get(root1) + size.get(root2));
            maxSize = Math.max(maxSize, size.get(root1));
        } else {
            parent.put(root1, root2);
            size.put(root2, size.get(root1) + size.get(root2));
            maxSize = Math.max(maxSize, size.get(root2));
        }
    }

    // 寻找树的根节点
    public int find(int node) {
        while(parent.get(node) != node) {
            int temp = parent.get(node);
            node = temp;
        }

        return node;
    } 
}