51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

java 算法模板

做题步骤 {#做题步骤}

  1. 先分析它的时间复杂度
  2. 出题人想要考什么算法
  3. 写代码
  4. 写测试数据

刷题网站 {#刷题网站}

https://www.dotcpp.com/oj/lanqiao/
https://www.lanqiao.cn/problems/

时间复杂度 {#时间复杂度}

https://www.acwing.com/blog/content/32/
时间限制为1s时:

  1. n ≤ 30 => O(2 ^n^ ), dfs+剪枝,状态压缩dp
  2. n ≤ 100 => O(n ^3^ ),floyd,dp,高斯消元
  3. n ≤ 1000 => O(n ^2^ ),O(n ^2^ logn),dp,二分,朴素版Dijkstra、朴素版Prim、Bellman-Ford
  4. n ≤ 10000 => O(n∗√n),块状链表、分块、莫队
  5. n ≤ 100000 => O(nlogn) => 各种sort,线段树、树状数组、set/map、heap、拓扑排序、dijkstra+heap、prim+heap、Kruskal、spfa、求凸包、求半平面交、二分、CDQ分治、整体二分、后缀数组、树链剖分、动态树
  6. n ≤ 1000000 => O(n), 以及常数较小的 O(nlogn) 算法 => 单调队列、 hash、双指针扫描、并查集,kmp、AC自动机,常数比较小的 O(nlogn) 的做法:sort、树状数组、heap、dijkstra、spfa
  7. n ≤ 10000000 => O(n),双指针扫描、kmp、AC自动机、线性筛素数
  8. n ≤ 10 ^9^ => O(√n),判断质数
  9. n ≤ 10 ^18^ => O(logn),最大公约数,快速幂,数位DP
  10. n ≤ 10 ^1000^ => O((logn) ^2^ ),高精度加减乘除
  11. n ≤ 10 ^100000^ => O(logk×loglogk),k表示位数,高精度加减、FFT/NTT

快读快写 {#快读快写}

  
   
* 
                          01
                        

   
* 
                          02
                        

   
* 
                          03
                        

   
* 
                          04
                        

   
* 
                          05
                        

   
* 
                          06
                        

   
* 
                          07
                        

   
* 
                          08
                        

   
* 
                          09
                        

   
* 
                          10
                        

   
* 
                          11
                        

   
* 
                          12
                        

   
* 
                          13
                        

   
* 
                          14
                        

   
* 
                          15
                        

   
* 
                          16
                        

   
* 
                          17
                        

   
* 
                          18
                        

  

                    
                          static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
    static StreamTokenizer cin = new StreamTokenizer(br);

    public static int nextInt() throws Exception {
        cin.nextToken();
        return (int) cin.nval;
    }

    public static double nextDouble() throws Exception {
        cin.nextToken();
        return cin.nval;
    }
    //只能读字母
    public static String next() throws Exception {
        cin.nextToken();
        return cin.sval;
    }

                    </code>
                  </pre>



 
基础算法 {#基础算法}
------------


 
### 整数二分 {#整数二分}


 
[https://oi-wiki.org/basic/binary/#二分答案](https://oi-wiki.org/basic/binary/#%E4%BA%8C%E5%88%86%E7%AD%94%E6%A1%88)

 
````````````````````````````````prism language-java
                    

    static boolean check(int x) {/* ... */} // 检查x是否满足某种性质

    // 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
    static int bsearch_1(int l, int r) {
        while (l &lt; r) {
            int mid = (l + r) &gt;&gt;&gt; 1;
            if (check(mid)) r = mid;    // check()判断mid是否满足性质
            else l = mid + 1;
        }
        return l;
    }

    // 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
    static int bsearch_2(int l, int r) {
        while (l &lt; r) {
            int mid = (l + r + 1) &gt;&gt;&gt; 1;
            if (check(mid)) l = mid;
            else r = mid - 1;
        }
        return l;
    }



                    </code>
                  </pre>



 
### 一维前缀和 {#一维前缀和}


 
[https://oi-wiki.org/basic/prefix-sum/#前缀和](https://oi-wiki.org/basic/prefix-sum/#%E5%89%8D%E7%BC%80%E5%92%8C)

 
                        
                          S[i] = a[1] + a[2] + ... a[i]
    a[l] + ... + a[r] = S[r] - S[l - 1]

                        </code>
                      </pre>



     
    ### 二维前缀和 {#二维前缀和}


     
                            
                              S[i, j] = 第i行j列格子左上部分所有元素的和
        以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:
        S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] + S[x1 - 1, y1 - 1]

                            </code>
                          </pre>



         
        ### 一维差分 {#一维差分}


         
                                
                                  给区间[l, r]中的每个数加上c:B[l] += c, B[r + 1] -= c

                                </code>
                              </pre>



             
            ### 二维差分 {#二维差分}


             
                                    
                                      给以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵中的所有元素加上c:
                S[x1, y1] += c, S[x2 + 1, y1] -= c, S[x1, y2 + 1] -= c, S[x2 + 1, y2 + 1] += c

                                    </code>
                                  </pre>



                 
                数据结构 {#数据结构}
                ------------


                 
                ### 单调栈 {#单调栈}


                 
                <https://oi-wiki.org/ds/monotonous-stack/>

                 
                常见模型:找出每个数左边离它最近的比它大/小的数

                 
                ```````````````````````````````prism language-java
                                    
                                      int tt = 0;
                int[] stk = new int[n + 1];
                for (int i = 1; i <= n; i++) {
                    while (tt > 0 && check(stk[tt], i)) {
                        tt--;
                    }
                    stk[++tt] = i;
                }

                                    </code>
                                  </pre>



                 
                ### 单调队列 {#单调队列}


                 
                <https://oi-wiki.org/ds/monotonous-queue/>

                 
                常见模型:找出滑动窗口中的最大值/最小值

                 
                ``````````````````````````````prism language-java
                                    
                                      int hh = 0, tt = -1;
                int[] q = new int[n];
                for (int i = 0; i < n; i++) {
                    while (hh <= tt && check_out(q[hh])) {
                        hh++;
                    }
                    while (hh <= tt && check(q[tt], i)) {
                        tt--;
                    }
                    q[++tt] = i;
                }

                                    </code>
                                  </pre>



                 
                ### KMP {#kmp}


                 
                <https://oi-wiki.org/string/kmp/>

                 
                求模式串的Next数组:

                 
                `````````````````````````````prism language-java
                                    
                                      int[] ne = new int[m + 1];
                ne[1] = 0;
                for (int i = 2, j = 0; i <= m; i++) {
                    while (j != 0 && p[i] != p[j + 1]) {
                        j = ne[j];
                    }
                    if (p[i] == p[j + 1]) {
                        j++;
                    }
                    ne[i] = j;
                }

                                    </code>
                                  </pre>



                 
                匹配:

                 
                ````````````````````````````prism language-java
                                    
                                      for (int i = 1, j = 0; i <= n; i++) {
                    while (j != 0 && s[i] != p[j + 1]) {
                        j = ne[j];
                    }
                    if (s[i] == p[j + 1]) {
                        j++;
                    }
                    if (j == m) {
                        j = ne[j];
                        // 匹配成功后的逻辑
                    }
                }

                                    </code>
                                  </pre>



                 
                ### 并查集 {#并查集}


                 
                <https://oi-wiki.org/ds/dsu/>

                 
                ```````````````````````````prism language-java
                                    
                                          static int n;
                    static int[] p = new int[n + 1]; // 存储每个点的祖宗节点

                    // 返回x的祖宗节点
                    static int find(int x) {
                        if (p[x] != x) {
                            p[x] = find(p[x]);
                        }
                        return p[x];
                    }

                    // 初始化,假定节点编号是1~n
                    static void init() {
                        for (int i = 1; i &lt;= n; i++) {
                            p[i] = i;
                        }
                    }

                    // 合并a和b所在的两个集合
                    static void union(int a, int b) {
                        p[find(a)] = find(b);
                    }

                                    </code>
                                  </pre>



                 
                ### 线段树 {#线段树}


                 
                <https://oi-wiki.org/ds/seg/>

                 
                ``````````````````````````prism language-java
                                    
                                          static int[] a;
                    static int[] d;

                    // 通过子节点计算父节点
                    static int pushUp(int l, int r) {
                        // 计算父节点的值
                        // ...
                    }

                    static void build(int s, int t, int p) {
                        // 对 [s,t] 区间建立线段树,当前根的编号为 p
                        if (s == t) {
                            d[p] = a[s];
                            return;
                        }
                        int m = s + ((t - s) &gt;&gt; 1);
                        // 移位运算符的优先级小于加减法,所以加上括号
                        // 如果写成 (s + t) &gt;&gt; 1 可能会超出 int 范围


                        build(s, m, p * 2);
                        build(m + 1, t, p * 2 + 1);
                        // 递归对左右区间建树
                        d[p] = pushUp(d[p * 2], d[(p * 2) + 1]);
                    }

                    static int getRes(int l, int r, int s, int t, int p) {
                        // [l, r] 为查询区间, [s, t] 为当前节点包含的区间, p 为当前节点的编号
                        if (l &lt;= s &amp;&amp; t &lt;= r) {
                            return d[p];  // 当前区间为询问区间的子集时直接返回当前区间的和
                        }
                        int m = s + ((t - s) &gt;&gt; 1), sum = 0;
                        int lv = 0, rv = 0; // 默认值
                        if (l &lt;= m) {
                            lv = getRes(l, r, s, m, p * 2);
                        }
                        // 如果左儿子代表的区间 [s, m] 与询问区间有交集, 则递归查询左儿子
                        if (r &gt; m) {
                            rv = getRes(l, r, m + 1, t, p * 2 + 1);
                        }

                        return pushUp(lv, rv);
                    }

                                    </code>
                                  </pre>



                 
                图论 {#图论}
                --------


                 
                ### 邻接矩阵 {#邻接矩阵}


                 
                [https://oi-wiki.org/graph/save/#邻接矩阵](https://oi-wiki.org/graph/save/#%E9%82%BB%E6%8E%A5%E7%9F%A9%E9%98%B5)

                 
                `````````````````````````prism language-java
                                    
                                          static int n;
                    static int[][] g = new int[n + 1][n + 1]; // 存储边a->b

                    // 添加一条边a-&gt;b
                    static void add(int a, int b) {
                        g[a][b] = 1;
                    }

                                    </code>
                                  </pre>



                 
                ### 邻接表 {#邻接表}


                 
                [https://oi-wiki.org/graph/save/#邻接矩阵](https://oi-wiki.org/graph/save/#%E9%82%BB%E6%8E%A5%E7%9F%A9%E9%98%B5)

                 
                ````````````````````````prism language-java
                                    
                                          //n个点,m条单向边
                    static int[] h = new int[n + 1]; // 存储每个点的单链表的头结点
                    static int[] e = new int[m + 1]; // 存储每个点的可达点
                    static int[] ne = new int[m + 1]; // 存储每个点的下一个可达点
                    static int idx;

                    // 添加一条边a-&gt;b
                    static void add(int a, int b) {
                        e[idx] = b;
                        ne[idx] = h[a];
                        h[a] = idx++;
                    }
                    // 初始化
                    static void init() {
                        Arrays.fill(h, -1);
                    }

                                    </code>
                                  </pre>



                 
                ### 深度优先遍历 {#深度优先遍历}


                 
                <https://oi-wiki.org/graph/dfs/>

                 
                ```````````````````````prism language-java
                                    
                                          static int n,m;
                    static boolean[] st = new boolean[n + 1]; // 表示点是否被遍历过
                    static int[] h = new int[n + 1], e = new int[m + 1], ne = new int[m + 1];
                    static void dfs(int u) {
                        st[u] = true; // 表示点u已经被遍历过

                        for (int i = h[u]; i != -1; i = ne[i]) {
                            int j = e[i];
                            if (!st[j]) {
                                dfs(j);
                            }
                        }
                    }

                                    </code>
                                  </pre>



                 
                ### 宽度优先遍历 {#宽度优先遍历}


                 
                <https://oi-wiki.org/graph/bfs/>

                 
                ``````````````````````prism language-java
                                    
                                      Queue<Integer> q = new LinkedList<>();
                boolean[] st = new boolean[n + 1]; // 表示点是否被遍历过

                st\[1\] = true; // 表示1号点已经被遍历过
                q.offer(1);


                while (!q.isEmpty()) {
                int t = q.poll();


                    for (int i = h[t]; i != -1; i = ne[i]) {
                        int j = e[i];
                        if (!st[j]) {
                            st[j] = true; // 表示点j已经被遍历过
                            q.offer(j);
                        }
                    }




                }


                                    </code>
                                  </pre>



                 
                ### 拓扑排序 {#拓扑排序}


                 
                <https://oi-wiki.org/graph/topo/>

                 
                `````````````````````prism language-java
                                    
                                      //n 表示点数
                Queue<Integer> q = new LinkedList<>(); // 存储拓扑序列
                int[] d = new int[n + 1]; // 存储点的入度

                // 统计每个点的入度
                for (int i = 1; i \<= n; i++) {
                for (int j = h\[i\]; j != -1; j = ne\[j\]) {
                int k = e\[j\];
                d\[k\]++;
                }
                }


                // 将入度为0的点入队
                for (int i = 1; i \<= n; i++) {
                if (d\[i\] == 0) {
                q.offer(i);
                }
                }


                while (!q.isEmpty()) {
                int t = q.poll();


                    for (int i = h[t]; i != -1; i = ne[i]) {
                        int j = e[i];
                        if (--d[j] == 0) {
                            q.offer(j);
                        }
                    }




                }


                // 如果所有点都入队了,说明存在拓扑序列;否则不存在拓扑序列。
                boolean hasTopologicalOrder = (tt == n - 1);


                                    </code>
                                  </pre>



                 
                ### 朴素Dijkstra算法 {#朴素dijkstra算法}


                 
                时间复杂度:O(n\^2 + m), 其中 n 表示点数,m 表示边数

                 
                [https://oi-wiki.org/graph/shortest-path/#dijkstra-算法](https://oi-wiki.org/graph/shortest-path/#dijkstra-%E7%AE%97%E6%B3%95)

                 
                ````````````````````prism language-java
                                    
                                          static int n;
                    static int[][] g = new int[n + 1][n + 1]; // 存储每条边
                    static int[] dist = new int[n + 1]; // 存储1号点到每个点的最短距离
                    static boolean[] st = new boolean[n + 1]; // 存储每个点的最短路是否已经确定

                    // 求1号点到n号点的最短路,如果不存在则返回-1
                    static int dijkstra() {
                        Arrays.fill(dist, 0x3f3f3f3f);
                        dist[1] = 0;

                        for (int i = 0; i &lt; n - 1; i++) {
                            int t = -1; // 在还未确定最短路的点中,寻找距离最小的点
                            for (int j = 1; j &lt;= n; j++) {
                                if (!st[j] &amp;&amp; (t == -1 || dist[t] &gt; dist[j])) {
                                    t = j;
                                }
                            }

                            // 用t更新其他点的距离
                            for (int j = 1; j &lt;= n; j++) {
                                dist[j] = Math.min(dist[j], dist[t] + g[t][j]);
                            }

                            st[t] = true;
                        }

                        if (dist[n] == 0x3f3f3f3f) {
                            return -1;
                        }
                        return dist[n];
                    }

                                    </code>
                                  </pre>



                 
                ### SPFA算法(队列优化的Bellman-Ford算法) {#spfa算法(队列优化的bellman-ford算法)}


                 
                时间复杂度:平均情况下 O(m), 最坏情况下 O(nm), 其中 n 表示点数,m 表示边数

                 
                [https://oi-wiki.org/graph/shortest-path/#队列优化spfa](https://oi-wiki.org/graph/shortest-path/#%E9%98%9F%E5%88%97%E4%BC%98%E5%8C%96spfa)

                 
                ```````````````````prism language-java
                                    
                                          static int n, m; // 总点数, 总边数
                    static int[] h = new int[n + 1];
                    static int[] w = new int[m + 1];
                    static int[] e = new int[m + 1];
                    static int[] ne = new int[m + 1];
                    static int[] dist = new int[n + 1]; // 存储每个点到1号点的最短距离
                    static boolean[] st = new boolean[n + 1]; // 存储每个点是否在队列中

                    // 求1号点到n号点的最短路距离,如果从1号点无法走到n号点则返回-1
                    static int spfa() {
                        Arrays.fill(dist, Integer.MAX_VALUE);
                        dist[1] = 0;

                        Queue&lt;Integer&gt; q = new LinkedList&lt;&gt;();
                        q.offer(1);
                        st[1] = true;

                        while (!q.isEmpty()) {
                            int t = q.poll();
                            st[t] = false;

                            for (int i = h[t]; i != -1; i = ne[i]) {
                                int j = e[i];
                                if (dist[j] &gt; dist[t] + w[i]) {
                                    dist[j] = dist[t] + w[i];
                                    if (!st[j]) { // 如果队列中已存在j,则不需要将j重复插入
                                        q.offer(j);
                                        st[j] = true;
                                    }


                                }
                            }
                        }

                        if (dist[n] == Integer.MAX_VALUE) {
                            return -1;
                        }
                        return dist[n];
                    }

                                    </code>
                                  </pre>



                 
                ### SPFA判断图中是否存在负环 {#spfa判断图中是否存在负环}


                 
                时间复杂度:O(nm), 其中 n 表示点数,m 表示边数

                 
                ``````````````````prism language-java
                                    
                                          static int n, m; // 总点数, 总边数
                    static int[] h = new int[n + 1];
                    static int[] w = new int[m + 1];
                    static int[] e = new int[m + 1];
                    static int[] ne = new int[m + 1];
                    static int[] dist = new int[n + 1]; // dist[x]存储1号点到x的最短距离
                    static int[] cnt = new int[n + 1]; // cnt[x]存储1到x的最短路中经过的点数
                    static boolean[] st = new boolean[n + 1]; // 存储每个点是否在队列中

                    // 如果存在负环,则返回true,否则返回false。
                    static boolean spfa() {
                        Queue&lt;Integer&gt; q = new LinkedList&lt;&gt;();
                        for (int i = 1; i &lt;= n; i++) {
                            q.offer(i);
                            st[i] = true;
                        }

                        while (!q.isEmpty()) {
                            int t = q.poll();
                            st[t] = false;

                            for (int i = h[t]; i != -1; i = ne[i]) {
                                int j = e[i];
                                if (dist[j] &gt; dist[t] + w[i]) {
                                    dist[j] = dist[t] + w[i];
                                    cnt[j] = cnt[t] + 1;
                                    if (cnt[j] &gt;= n) { // 如果从1号点到x的最短路中包含至少n个点(不包括自己),则说明存在环
                                        return true;
                                    }
                                    if (!st[j]) {
                                        q.offer(j);
                                        st[j] = true;
                                    }
                                }
                            }
                        }

                        return false;
                    }

                                    </code>
                                  </pre>



                 
                ### Floyd算法 {#floyd算法}


                 
                时间复杂度:O(n\^3), 其中 n 表示点数

                 
                [https://oi-wiki.org/graph/shortest-path/#floyd-算法](https://oi-wiki.org/graph/shortest-path/#floyd-%E7%AE%97%E6%B3%95)

                 
                `````````````````prism language-java
                                    
                                          static final int INF = 0x3f3f3f3f;
                    static int n; // 总点数
                    static int[][] d = new int[n + 1][n + 1]; // 存储最短距离

                    // 初始化
                    static void init() {
                        for (int i = 1; i &lt;= n; i++) {
                            for (int j = 1; j &lt;= n; j++) {
                                if (i == j) {
                                    d[i][j] = 0;
                                } else {
                                    d[i][j] = INF;
                                }
                            }
                        }
                    }

                    // 算法结束后,d[a][b]表示a到b的最短距离
                    static void floyd() {
                        for (int k = 1; k &lt;= n; k++) {
                            for (int i = 1; i &lt;= n; i++) {
                                for (int j = 1; j &lt;= n; j++) {
                                    d[i][j] = Math.min(d[i][j], d[i][k] + d[k][j]);
                                }
                            }
                        }
                    }

                                    </code>
                                  </pre>



                 
                ### 朴素版Prim算法 {#朴素版prim算法}


                 
                时间复杂度:O(n\^2 + m), 其中 n 表示点数,m 表示边数

                 
                [https://oi-wiki.org/graph/mst/#prim-算法](https://oi-wiki.org/graph/mst/#prim-%E7%AE%97%E6%B3%95)

                 
                ````````````````prism language-java
                                    
                                          static final int INF = 0x3f3f3f3f;
                    static int n; // n表示点数
                    static int[][] g = new int[n + 1][n + 1]; // 邻接矩阵,存储所有边
                    static int[] dist = new int[n + 1]; //

                    // 存储其他点到当前最小生成树的距离
                    static boolean[] st = new boolean[n + 1]; // 存储每个点是否已经在生成树中

                    // 如果图不连通,则返回INF(值是0x3f3f3f3f),否则返回最小生成树的树边权重之和
                    static int prim() {
                        Arrays.fill(dist, INF);

                        int res = 0;
                        for (int i = 0; i &lt; n; i++) {
                            int t = -1;
                            for (int j = 1; j &lt;= n; j++) {
                                if (!st[j] &amp;&amp; (t == -1 || dist[t] &gt; dist[j])) {
                                    t = j;
                                }
                            }

                            if (i != 0 &amp;&amp; dist[t] == INF) {
                                return INF;
                            }

                            if (i != 0) {
                                res += dist[t];
                            }
                            st[t] = true;

                            for (int j = 1; j &lt;= n; j++) {
                                dist[j] = Math.min(dist[j], g[t][j]);
                            }
                        }

                        return res;
                    }

                                    </code>
                                  </pre>



                 
                ### Kruskal 算法 {#kruskal-算法}


                 
                时间复杂度:O(m \* log(m)), 其中 n 表示点数,m 表示边数

                 
                [https://oi-wiki.org/graph/mst/#kruskal-算法](https://oi-wiki.org/graph/mst/#kruskal-%E7%AE%97%E6%B3%95)

                 
                ```````````````prism language-java
                                    
                                          static int n, m; // n是点数,m是边数
                    static int[] p = new int[n + 1];
                    static int[][] e = new int[m + 1][3];
                    public static int find(int x) { // 并查集核心操作
                        if (p[x] != x) {
                            p[x] = find(p[x]);
                        }
                        return p[x];
                    }
                    public static int kruskal() {
                        Arrays.sort(e, 0, m, (o1, o2) -> o1[2] - o2[2]);

                        for (int i = 1; i &lt;= n; i++) {
                            p[i] = i; // 初始化并查集
                        }

                        int res = 0, cnt = 0;
                        for (int i = 0; i &lt; m; i++) {
                            int a = e[i][0], b = e[i][1], w = e[i][2];

                            a = find(a);
                            b = find(b);
                            if (a != b) { // 如果两个连通块不连通,则将这两个连通块合并
                                p[a] = b;
                                res += w;
                                cnt++;
                            }
                        }
                        //没有最小生成树
                        if (cnt &lt; n - 1) {
                            return Integer.MAX_VALUE;
                        }
                        return res;
                    }

                                    </code>
                                  </pre>



                 
                数学知识 {#数学知识}
                ------------


                 
                ### 试除法判定质数 {#试除法判定质数}


                 
                ``````````````prism language-java
                                    
                                          static boolean isPrime(int x) {
                        if (x < 2) return false;
                        for (int i = 2; i <= x / i; i++) {
                            if (x % i == 0) {
                                return false;
                            }
                        }
                        return true;
                    }

                                    </code>
                                  </pre>



                 
                ### 试除法分解质因数 {#试除法分解质因数}


                 
                `````````````prism language-java
                                    
                                          static void divide(int x) {
                        for (int i = 2; i <= x / i; i++) {
                            if (x % i == 0) {
                                int s = 0;
                                while (x % i == 0) {
                                    x /= i;
                                    s++;
                                }
                                System.out.println(i + " " + s);
                            }
                        }
                        if (x > 1) {
                            System.out.println(x + " " + 1);
                        }
                        System.out.println();
                    }

                                    </code>
                                  </pre>



                 
                ### 线性筛法求素数 {#线性筛法求素数}


                 
                [https://oi-wiki.org/math/number-theory/sieve/#素数筛法](https://oi-wiki.org/math/number-theory/sieve/#%E7%B4%A0%E6%95%B0%E7%AD%9B%E6%B3%95)

                 
                ````````````prism language-java
                                    
                                          static int n;//求小于等于n的素数
                    static int[] primes = new int[n + 1]; // 存储所有素数
                    static boolean[] st = new boolean[n + 1]; // 存储x是否被筛掉
                    static int cnt = 0;

                    static void getPrimes(int n) {
                        for (int i = 2; i &lt;= n; i++) {
                            if (!st[i]) {
                                primes[cnt++] = i;
                            }
                            for (int j = 0; j &lt; cnt &amp;&amp; primes[j] &lt;= n / i; j++) {
                                st[primes[j] * i] = true;
                                if (i % primes[j] == 0) {
                                    break;
                                }
                            }
                        }
                    }

                                    </code>
                                  </pre>



                 
                ### 试除法求所有约数 {#试除法求所有约数}


                 
                ```````````prism language-java
                                    
                                          static List<Integer> getDivisors(int x) {
                        List<Integer> res = new ArrayList<>();
                        for (int i = 1; i <= x / i; i++) {
                            if (x % i == 0) {
                                res.add(i);
                                if (i != x / i) {
                                    res.add(x / i);
                                }
                            }
                        }
                        Collections.sort(res);
                        return res;
                    }

                                    </code>
                                  </pre>



                 
                ### 约数个数和约数之和 {#约数个数和约数之和}


                 
                如果 N = p1\^c1 \* p2\^c2 \* ... \* pk\^ck \* 
                 约数个数: (c1 + 1) \* (c2 + 1) \* ... \* (ck + 1) \* 
                 约数之和: (p1\^0 + p1\^1 + ... + p1\^c1) \* ... \* (pk\^0 + pk\^1 + ... + pk\^ck)

                 
                ### 欧几里得算法 {#欧几里得算法}


                 
                求最大公因数

                 
                [https://oi-wiki.org/math/number-theory/gcd/#欧几里得算法](https://oi-wiki.org/math/number-theory/gcd/#%E6%AC%A7%E5%87%A0%E9%87%8C%E5%BE%97%E7%AE%97%E6%B3%95)

                 
                ``````````prism language-java
                                    
                                          static int gcd(int a, int b) {
                        return b == 0 ? a : gcd(b, a % b);
                    }

                                    </code>
                                  </pre>



                 
                ### 求欧拉函数 {#求欧拉函数}


                 
                欧拉函数,即  \varphi(n)  ,表示的是小于等于  n  和  n  互质的数的个数。

                 
                <https://oi-wiki.org/math/number-theory/euler/>

                 
                `````````prism language-java
                                    
                                          static int phi(int x) {
                        int res = x;
                        for (int i = 2; i <= x / i; i++) {
                            if (x % i == 0) {
                                res = res / i * (i - 1);
                                while (x % i == 0) {
                                    x /= i;
                                }
                            }
                        }
                        if (x > 1) {
                            res = res / x * (x - 1);
                        }
                        return res;
                    }

                                    </code>
                                  </pre>



                 
                ### 筛法求欧拉函数 {#筛法求欧拉函数}


                 
                [https://oi-wiki.org/math/number-theory/sieve/#筛法求欧拉函数](https://oi-wiki.org/math/number-theory/sieve/#%E7%AD%9B%E6%B3%95%E6%B1%82%E6%AC%A7%E6%8B%89%E5%87%BD%E6%95%B0)

                 
                ````````prism language-java
                                    
                                          static int n;
                    static int[] primes = new int[n + 1]; // 存储所有素数
                    static int[] euler = new int[n + 1]; // 存储每个数的欧拉函数
                    static boolean[] st = new boolean[n + 1]; // 存储x是否被筛掉
                    static int cnt; // 存储素数的个数
                    static void getEulers(int n) {
                        euler[1] = 1;
                        for (int i = 2; i <= n; i++) {
                            if (!st[i]) {
                                primes[cnt++] = i;
                                euler[i] = i - 1;
                            }
                            for (int j = 0; j < cnt && primes[j] <= n / i; j++) {
                                int t = primes[j] * i;
                                st[t] = true;
                                if (i % primes[j] == 0) {
                                    euler[t] = euler[i] * primes[j];
                                    break;
                                }
                                euler[t] = euler[i] * (primes[j] - 1);
                            }
                        }
                    }

                                    </code>
                                  </pre>



                 
                ### 快速幂 {#快速幂}


                 
                求 m\^k mod p,时间复杂度 O(log(k))。

                 
                qmi(m, p - 2, p) 等于m模p下的乘法逆元

                 
                <https://oi-wiki.org/math/binary-exponentiation/>

                 
                ```````prism language-java
                                    
                                          static int qmi(int m, int k, int p) {
                        int res = 1 % p;
                        int t = m;
                        while (k != 0) {
                            if ((k & 1) == 1) {
                                res = res * t % p;
                            }
                            t = t * t % p;
                            k >>>= 1;
                        }
                        return res;
                    }

                                    </code>
                                  </pre>



                 
                ### 扩展欧几里得算法 {#扩展欧几里得算法}


                 
                求x, y,使得ax + by = gcd(a, b)

                 
                若exgcd(a, b, x, y) == 1 则 (x\[0\]%b+b)%b 是a模b下的乘法逆元

                 
                [https://oi-wiki.org/math/number-theory/gcd/#扩展欧几里得算法](https://oi-wiki.org/math/number-theory/gcd/#%E6%89%A9%E5%B1%95%E6%AC%A7%E5%87%A0%E9%87%8C%E5%BE%97%E7%AE%97%E6%B3%95)

                 
                ``````prism language-java
                                    
                                          static int exgcd(int a, int b, int[] x, int[] y) {
                        if (b == 0) {
                            x[0] = 1;
                            y[0] = 0;
                            return a;
                        }
                        int d = exgcd(b, a % b, y, x);
                        y[0] -= (a/b) * x[0];
                        return d;
                    }

                                    </code>
                                  </pre>



                 
                ### 线性求乘法逆元 {#线性求乘法逆元}


                 
                <https://oi-wiki.org/math/number-theory/inverse/>

                 
                `````prism language-java
                                    
                                          static int n, p;
                    static long[] inv = new long[n + 1];

                    static void get() {
                        
                        inv[1] = 1;
                        for (int i = 2; i &lt;= n; ++i) {
                            inv[i] = (long)(p - p / i) * inv[p % i] % p;
                        }g
                    }

                                    </code>
                                  </pre>



                 
                贪心 {#贪心}
                --------


                 
                <https://oi-wiki.org/basic/greedy/>

                 
                每一步行动总是按某种指标选取最优的操作。

                 
                动态规划 {#动态规划}
                ------------


                 
                ### 分析法 {#分析法}


                 
                ````prism language-mermaid
                                    
                                      graph LR

                A\[定义问题\] --\> B\[确定子问题\]
                B --\> C\[确定状态\]
                C --\> D\[确定状态转移方程\]
                D --\> E\[确定初始条件和边界条件\]
                E --\> F\[计算最优解\]
                F --\> G\[返回最优解\]


                                    </code>
                                  </pre>



                 
                ### 闫氏dp分析法 {#闫氏dp分析法}


                 
                <https://www.acwing.com/file_system/file/content/whole/index/content/1084122/>

                 
                ```prism language-mermaid
                                    
                                      graph LR

                A\[问题\] --\> B\[状态表示\]
                B --\> C\[集合\]
                B --\> D\[属性\]
                A --\> E\[状态计算\]


                                    </code>
                                  </pre>



                 
                内容参考 {#内容参考}
                ------------


                 
                ​ <https://oi-wiki.org>

                 
                ​ <https://www.acwing.com/activity/content/introduction/11/>


                ```


                ````


                `````


                ``````


                ```````


                ````````


                `````````


                ``````````


                ```````````


                ````````````


                `````````````


                ``````````````


                ```````````````


                ````````````````


                `````````````````


                ``````````````````


                ```````````````````


                ````````````````````


                `````````````````````


                ``````````````````````


                ```````````````````````


                ````````````````````````


                `````````````````````````


                ``````````````````````````


                ```````````````````````````


                ````````````````````````````


                `````````````````````````````


                ``````````````````````````````


                ```````````````````````````````










````````````````````````````````

赞(1)
未经允许不得转载:工具盒子 » java 算法模板