[{"data":1,"prerenderedAt":594},["ShallowReactive",2],{"/2026/acm-java-template":3,"surround-/2026/acm-java-template":583},{"id":4,"title":5,"body":6,"categories":556,"comment":558,"date":559,"description":560,"donation":558,"draft":561,"extension":562,"image":563,"meta":564,"navigation":558,"path":566,"permalink":563,"postfooter":558,"published":563,"readingTime":567,"recommend":563,"references":563,"seo":572,"sitemap":573,"stem":574,"tags":575,"type":581,"updated":559,"__hash__":582},"content/posts/2026/acm-java-template.md","Java 的 ACM 常用模板",{"type":7,"value":8,"toc":541},"minimark",[9,23,26,54,61,65,68,78,81,130,140,159,164,179,182,207,210,213,219,222,228,235,237,247,253,259,277,280,294,297,299,305,308,328,331,337,340,343,349,352,358,361,364,367,373,380,386,389,392,395,401,404,411,417,420,426,429,435,438,442,445,456,462,468,472,475,489,492,498,509,515,518,529,532,535],[10,11,14],"alert",{"title":12,"type":13},"适用场景","info",[15,16,17,18,22],"p",{},"本文默认面向 OJ / ACM 模式：单文件提交、类名为 ",[19,20,21],"code",{"code":21},"Main","、通过标准输入输出读写数据。",[15,24,25],{},"这篇就整理一份我觉得比较实用的 Java ACM 模板，包括以下内容：",[27,28,29,33,36,39,42,45,48,51],"ul",{},[30,31,32],"li",{},"快读",[30,34,35],{},"快写",[30,37,38],{},"多组样例写法",[30,40,41],{},"常用数学模块",[30,43,44],{},"二分模板",[30,46,47],{},"前缀和 / 差分",[30,49,50],{},"并查集",[30,52,53],{},"图的 BFS / DFS",[15,55,56,57],{},"我建议直接粘贴：",[58,59,60],"strong",{},"背熟一份模板，往里CV，现写太累了。",[62,63,64],"h2",{"id":64},"先是基础模板",[15,66,67],{},"最简洁的版本：",[69,70,76],"pre",{"className":71,"code":73,"language":74,"meta":75},[72],"language-java","import java.io.*;\nimport java.util.*;\n\npublic class Main {\n    static FastScanner fs = new FastScanner(System.in);\n    static PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));\n    static StringBuilder sb = new StringBuilder();\n\n    public static void main(String[] args) throws Exception {\n        int T = 1;\n        // T = fs.nextInt();\n        while (T-- > 0) {\n            solve();\n        }\n        out.print(sb);\n        out.flush();\n    }\n\n    static void solve() throws Exception {\n        int n = fs.nextInt();\n        long sum = 0;\n        for (int i = 0; i \u003C n; i++) {\n            sum += fs.nextLong();\n        }\n        sb.append(sum).append('\\n');\n    }\n\n    static class FastScanner {\n        private final InputStream in;\n        private final byte[] buffer = new byte[1 \u003C\u003C 16];\n        private int ptr = 0;\n        private int len = 0;\n\n        FastScanner(InputStream is) {\n            in = is;\n        }\n\n        private int read() throws IOException {\n            if (ptr >= len) {\n                len = in.read(buffer);\n                ptr = 0;\n                if (len \u003C= 0) {\n                    return -1;\n                }\n            }\n            return buffer[ptr++];\n        }\n\n        String next() throws IOException {\n            StringBuilder s = new StringBuilder();\n            int c;\n            while ((c = read()) != -1 && c \u003C= ' ') {\n                // skip blank\n            }\n            if (c == -1) {\n                return null;\n            }\n            do {\n                s.append((char) c);\n                c = read();\n            } while (c > ' ');\n            return s.toString();\n        }\n\n        String nextLine() throws IOException {\n            StringBuilder s = new StringBuilder();\n            int c = read();\n            if (c == -1) {\n                return null;\n            }\n            while (c != -1 && c != '\\n') {\n                if (c != '\\r') {\n                    s.append((char) c);\n                }\n                c = read();\n            }\n            return s.toString();\n        }\n\n        int nextInt() throws IOException {\n            int c;\n            while ((c = read()) != -1 && c \u003C= ' ') {\n                // skip blank\n            }\n            if (c == -1) {\n                throw new EOFException();\n            }\n            int sign = 1;\n            if (c == '-') {\n                sign = -1;\n                c = read();\n            }\n            int val = 0;\n            while (c > ' ') {\n                val = val * 10 + c - '0';\n                c = read();\n            }\n            return val * sign;\n        }\n\n        long nextLong() throws IOException {\n            int c;\n            while ((c = read()) != -1 && c \u003C= ' ') {\n                // skip blank\n            }\n            if (c == -1) {\n                throw new EOFException();\n            }\n            int sign = 1;\n            if (c == '-') {\n                sign = -1;\n                c = read();\n            }\n            long val = 0;\n            while (c > ' ') {\n                val = val * 10 + c - '0';\n                c = read();\n            }\n            return val * sign;\n        }\n\n        double nextDouble() throws IOException {\n            return Double.parseDouble(next());\n        }\n    }\n}\n","java","",[19,77,73],{"__ignoreMap":75},[15,79,80],{},"关于这个模板：",[27,82,83,93,104,110],{},[30,84,85,88,89,92],{},[19,86,87],{"code":87},"FastScanner"," 用字节流读，通常比 ",[19,90,91],{"code":91},"Scanner"," 快。",[30,94,95,96,99,100,103],{},"输出统一先写进 ",[19,97,98],{"code":98},"StringBuilder","，最后一次性 ",[19,101,102],{"code":102},"flush","。",[30,105,106,109],{},[19,107,108],{"code":108},"solve()"," 单独拆出来，多组样例时更顺手。",[30,111,112,113,116,117,116,120,116,123,126,127,103],{},"默认支持 ",[19,114,115],{"code":115},"int","、",[19,118,119],{"code":119},"long",[19,121,122],{"code":122},"double",[19,124,125],{"code":125},"String","，以及按整行读取的 ",[19,128,129],{"code":129},"nextLine()",[15,131,132,133,136,137,139],{},"如果题目的输出不多，其实也可以直接 ",[19,134,135],{"code":135},"out.println(ans)","；但如果输出很多行，我更建议像上面这样统一 append 到 ",[19,138,98],{"code":98}," 里再输出。",[15,141,142,143,145,146,116,149,152,153,155,156,158],{},"补一句使用上的注意点：",[19,144,129],{"code":129}," 适合读整行文本，但如果你前面刚调用过 ",[19,147,148],{"code":148},"nextInt()",[19,150,151],{"code":151},"nextLong()"," 这类按 token 读取的方法，紧接着 ",[19,154,129],{"code":129}," 读到的可能会是当前行剩余的空串。遇到这种题时，要么统一按行读取后自己拆分，要么先额外调用一次 ",[19,157,129],{"code":129}," 吃掉换行。",[15,160,161,163],{},[19,162,91],{"code":91}," 的优点是写起来直观，但它在 OJ 场景里有两个明显问题：",[165,166,167,173],"ol",{},[30,168,169,172],{},[58,170,171],{},"速度通常不够快","，大输入量题目容易卡常。",[30,174,175,178],{},[58,176,177],{},"封装层级比较高","，在高频读数字时开销更大。",[15,180,181],{},"所以如果是笔试、竞赛、OJ 刷题，一般建议：",[27,183,184,193,198],{},[30,185,186,189,190],{},[19,187,188],{"code":188},"BufferedInputStream"," / ",[19,191,192],{"code":192},"BufferedReader",[30,194,195,196],{},"自己写 ",[19,197,87],{"code":87},[30,199,200,201,204,205],{},"输出用 ",[19,202,203],{"code":203},"PrintWriter"," 或 ",[19,206,98],{"code":98},[62,208,209],{"id":209},"多组样例模板",[15,211,212],{},"很多题目是多测，推荐固定成下面这种结构：",[69,214,217],{"className":215,"code":216,"language":74,"meta":75},[72],"public static void main(String[] args) throws Exception {\n    int T = fs.nextInt();\n    while (T-- > 0) {\n        solve();\n    }\n    out.print(sb);\n    out.flush();\n}\n",[19,218,216],{"__ignoreMap":75},[15,220,221],{},"如果题目不是多测，就保留：",[69,223,226],{"className":224,"code":225,"language":74,"meta":75},[72],"int T = 1;\n",[19,227,225],{"__ignoreMap":75},[15,229,230,231,234],{},"只改 ",[19,232,233],{"code":233},"T"," 的读取方式就行。",[62,236,41],{"id":41},[15,238,239,240,116,243,246],{},"常用的几个数学模块: ",[19,241,242],{"code":242},"gcd",[19,244,245],{"code":245},"lcm"," 、快速幂。",[69,248,251],{"className":249,"code":250,"language":74,"meta":75},[72],"static long gcd(long a, long b) {\n    while (b != 0) {\n        long t = a % b;\n        a = b;\n        b = t;\n    }\n    return a;\n}\n\nstatic long lcm(long a, long b) {\n    return a / gcd(a, b) * b;\n}\n\nstatic long qpow(long a, long b, long mod) {\n    long res = 1 % mod;\n    a %= mod;\n    while (b > 0) {\n        if ((b & 1) == 1) {\n            res = res * a % mod;\n        }\n        a = a * a % mod;\n        b >>= 1;\n    }\n    return res;\n}\n",[19,252,250],{"__ignoreMap":75},[254,255,256],"blockquote",{},[15,257,258],{},"是什么",[27,260,261,266,271],{},[30,262,263,265],{},[19,264,242],{"code":242},"：最大公约数",[30,267,268,270],{},[19,269,245],{"code":245},"：最小公倍数",[30,272,273,276],{},[19,274,275],{"code":275},"qpow","：快速幂，常用于取模幂运算",[15,278,279],{},"如果题目里出现：",[27,281,282,285,288,291],{},[30,283,284],{},"“最大公约数”",[30,286,287],{},"“最小公倍数”",[30,289,290],{},"“模意义下的幂”",[30,292,293],{},"“组合数预处理里的逆元”",[15,295,296],{},"这几个函数基本都能直接派上用场。",[62,298,44],{"id":44},[69,300,303],{"className":301,"code":302,"language":74,"meta":75},[72],"static int lowerBound(int[] a, int target) {\n    int l = 0, r = a.length;\n    while (l \u003C r) {\n        int mid = (l + r) >>> 1;\n        if (a[mid] >= target) {\n            r = mid;\n        } else {\n            l = mid + 1;\n        }\n    }\n    return l;\n}\n\nstatic int upperBound(int[] a, int target) {\n    int l = 0, r = a.length;\n    while (l \u003C r) {\n        int mid = (l + r) >>> 1;\n        if (a[mid] > target) {\n            r = mid;\n        } else {\n            l = mid + 1;\n        }\n    }\n    return l;\n}\n",[19,304,302],{"__ignoreMap":75},[15,306,307],{},"其中：",[27,309,310,320],{},[30,311,312,315,316,319],{},[19,313,314],{"code":314},"lowerBound(a, x)","：返回第一个 ",[19,317,318],{"code":318},">= x"," 的位置",[30,321,322,315,325,319],{},[19,323,324],{"code":324},"upperBound(a, x)",[19,326,327],{"code":327},"> x",[15,329,330],{},"如果你做的是“答案二分”，也建议把判定函数拆出来：",[69,332,335],{"className":333,"code":334,"language":74,"meta":75},[72],"static boolean check(long mid) {\n    return true;\n}\n\nstatic long binarySearch(long l, long r) {\n    while (l \u003C r) {\n        long mid = (l + r) >>> 1;\n        if (check(mid)) {\n            r = mid;\n        } else {\n            l = mid + 1;\n        }\n    }\n    return l;\n}\n",[19,336,334],{"__ignoreMap":75},[15,338,339],{},"这样比把判定逻辑全部塞进 while 里更不容易写乱。",[62,341,342],{"id":342},"前缀和模板",[69,344,347],{"className":345,"code":346,"language":74,"meta":75},[72],"static long[] prefixSum(int[] a) {\n    int n = a.length;\n    long[] pre = new long[n + 1];\n    for (int i = 0; i \u003C n; i++) {\n        pre[i + 1] = pre[i] + a[i];\n    }\n    return pre;\n}\n",[19,348,346],{"__ignoreMap":75},[15,350,351],{},"例如：",[69,353,356],{"className":354,"code":355,"language":74,"meta":75},[72],"long[] pre = prefixSum(a);\nlong sum = pre[r + 1] - pre[l];\n",[19,357,355],{"__ignoreMap":75},[15,359,360],{},"这个写法简单、稳定，而且不容易出现 off-by-one 错误。",[62,362,363],{"id":363},"差分模板",[15,365,366],{},"如果题目有大量“区间加”的操作，差分会比每次暴力修改快很多。",[69,368,371],{"className":369,"code":370,"language":74,"meta":75},[72],"static void rangeAdd(int[] diff, int l, int r, int val) {\n    diff[l] += val;\n    if (r + 1 \u003C diff.length) {\n        diff[r + 1] -= val;\n    }\n}\n\nstatic int[] buildArray(int[] diff) {\n    int[] a = new int[diff.length];\n    a[0] = diff[0];\n    for (int i = 1; i \u003C diff.length; i++) {\n        a[i] = a[i - 1] + diff[i];\n    }\n    return a;\n}\n",[19,372,370],{"__ignoreMap":75},[15,374,375,376,379],{},"如果原数组长度是 ",[19,377,378],{"code":378},"n","，可以：",[69,381,384],{"className":382,"code":383,"language":74,"meta":75},[72],"int[] diff = new int[n];\nrangeAdd(diff, l, r, val);\nrangeAdd(diff, l2, r2, val2);\nint[] a = buildArray(diff);\n",[19,385,383],{"__ignoreMap":75},[15,387,388],{},"当题目是“做很多次区间修改，最后统一求结果”时，这套模板很常用。",[62,390,391],{"id":391},"并查集模板",[15,393,394],{},"只要题目里出现“连通块”“合并集合”“判断是否属于同一组”，基本就是并查集。",[69,396,399],{"className":397,"code":398,"language":74,"meta":75},[72],"static class DSU {\n    int[] parent;\n    int[] size;\n\n    DSU(int n) {\n        parent = new int[n + 1];\n        size = new int[n + 1];\n        for (int i = 1; i \u003C= n; i++) {\n            parent[i] = i;\n            size[i] = 1;\n        }\n    }\n\n    int find(int x) {\n        if (parent[x] != x) {\n            parent[x] = find(parent[x]);\n        }\n        return parent[x];\n    }\n\n    boolean union(int a, int b) {\n        int fa = find(a);\n        int fb = find(b);\n        if (fa == fb) {\n            return false;\n        }\n        if (size[fa] \u003C size[fb]) {\n            int t = fa;\n            fa = fb;\n            fb = t;\n        }\n        parent[fb] = fa;\n        size[fa] += size[fb];\n        return true;\n    }\n\n    boolean same(int a, int b) {\n        return find(a) == find(b);\n    }\n}\n",[19,400,398],{"__ignoreMap":75},[62,402,403],{"id":403},"邻接表模板",[15,405,406,407,410],{},"对于大多数图题，",[19,408,409],{"code":409},"List\u003CInteger>[]"," 这种邻接表写法已经够顺手了。",[69,412,415],{"className":413,"code":414,"language":74,"meta":75},[72],"static List\u003CInteger>[] buildGraph(int n) {\n    List\u003CInteger>[] g = new ArrayList[n + 1];\n    for (int i = 1; i \u003C= n; i++) {\n        g[i] = new ArrayList\u003C>();\n    }\n    return g;\n}\n\nstatic void addUndirectedEdge(List\u003CInteger>[] g, int u, int v) {\n    g[u].add(v);\n    g[v].add(u);\n}\n\nstatic void addDirectedEdge(List\u003CInteger>[] g, int u, int v) {\n    g[u].add(v);\n}\n",[19,416,414],{"__ignoreMap":75},[15,418,419],{},"如果是带权图，可以把边单独封成类：",[69,421,424],{"className":422,"code":423,"language":74,"meta":75},[72],"static class Edge {\n    int to;\n    int w;\n\n    Edge(int to, int w) {\n        this.to = to;\n        this.w = w;\n    }\n}\n",[19,425,423],{"__ignoreMap":75},[15,427,428],{},"然后把图改成：",[69,430,433],{"className":431,"code":432,"language":74,"meta":75},[72],"static List\u003CEdge>[] g;\n",[19,434,432],{"__ignoreMap":75},[15,436,437],{},"这样 Dijkstra、最短路、树上遍历都会更方便。",[62,439,441],{"id":440},"bfs-模板","BFS 模板",[15,443,444],{},"BFS 最常见的用途是：",[27,446,447,450,453],{},[30,448,449],{},"无权图最短路",[30,451,452],{},"分层遍历",[30,454,455],{},"从起点向外扩散",[69,457,460],{"className":458,"code":459,"language":74,"meta":75},[72],"static int[] bfs(List\u003CInteger>[] g, int start) {\n    int n = g.length - 1;\n    int[] dist = new int[n + 1];\n    Arrays.fill(dist, -1);\n\n    ArrayDeque\u003CInteger> q = new ArrayDeque\u003C>();\n    q.offer(start);\n    dist[start] = 0;\n\n    while (!q.isEmpty()) {\n        int u = q.poll();\n        for (int v : g[u]) {\n            if (dist[v] != -1) {\n                continue;\n            }\n            dist[v] = dist[u] + 1;\n            q.offer(v);\n        }\n    }\n    return dist;\n}\n",[19,461,459],{"__ignoreMap":75},[15,463,464,467],{},[19,465,466],{"code":466},"dist[v] == -1"," 既可以表示“没访问过”，也能顺手记录最短距离，写起来很省事。",[62,469,471],{"id":470},"dfs-模板","DFS 模板",[15,473,474],{},"如果题目是：",[27,476,477,480,483,486],{},[30,478,479],{},"遍历整棵树",[30,481,482],{},"搜索联通块",[30,484,485],{},"递归枚举",[30,487,488],{},"回溯",[15,490,491],{},"DFS 一般就足够了。",[69,493,496],{"className":494,"code":495,"language":74,"meta":75},[72],"static void dfs(int u, int parent, List\u003CInteger>[] g, boolean[] vis) {\n    vis[u] = true;\n    for (int v : g[u]) {\n        if (v == parent) {\n            continue;\n        }\n        if (!vis[v]) {\n            dfs(v, u, g, vis);\n        }\n    }\n}\n",[19,497,495],{"__ignoreMap":75},[15,499,500,501,504,505,508],{},"如果题目图不一定是树，",[19,502,503],{"code":503},"parent"," 参数可以去掉，只保留 ",[19,506,507],{"code":507},"vis"," 判重即可。",[15,510,511,512],{},"不过也要注意：",[58,513,514],{},"Java 递归层数深时可能栈溢出。",[15,516,517],{},"所以当你遇到：",[27,519,520,523,526],{},[30,521,522],{},"链很长的树",[30,524,525],{},"深度很深的图",[30,527,528],{},"数据规模很大",[15,530,531],{},"就要考虑把递归 DFS 改成显式栈的迭代写法。",[62,533,534],{"id":534},"最后",[15,536,537],{},[538,539,540],"em",{},"没了",{"title":75,"searchDepth":542,"depth":542,"links":543},4,[544,546,547,548,549,550,551,552,553,554,555],{"id":64,"depth":545,"text":64},2,{"id":209,"depth":545,"text":209},{"id":41,"depth":545,"text":41},{"id":44,"depth":545,"text":44},{"id":342,"depth":545,"text":342},{"id":363,"depth":545,"text":363},{"id":391,"depth":545,"text":391},{"id":403,"depth":545,"text":403},{"id":440,"depth":545,"text":441},{"id":470,"depth":545,"text":471},{"id":534,"depth":545,"text":534},[557],"开发",true,"2026-04-04 15:36:18","整理一份适合 OJ / ACM 模式提交的 Java 模板，包含快读、快写，以及数学、二分、前缀和、并查集、图遍历等常用模块。",false,"md",null,{"slots":565},{},"/2026/acm-java-template",{"text":568,"minutes":569,"time":570,"words":571},"11 min read",10.225,613500,2045,{"title":5,"description":560},{"loc":566},"posts/2026/acm-java-template",[576,577,578,579,580],"Java","ACM","OJ","算法竞赛","模板","tech","lKOG7UVbD1fH50_w1-2t56enjpTNTVKsUu3fHDelDbw",[584,589],{"title":585,"path":586,"stem":587,"date":588,"type":581,"children":-1},"Function Calling、MCP、Skill、Tool","/2026/function-call-mcp-skill-tool","posts/2026/function-call-mcp-skill-tool","2026-04-03 15:12:00",{"title":590,"path":591,"stem":592,"date":593,"type":581,"children":-1},"Harness Engineering","/2026/harness-engineering","posts/2026/harness-engineering","2026-04-09 22:20:00",1778311048012]