import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintWriter; import java.math.BigInteger; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Deque; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.PriorityQueue; import java.util.Queue; import java.util.Scanner; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import java.util.function.BiFunction; public class Sample { public static void main(String[] args) throws Exception { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); sc.close(); for (int i = 0; i < n; i++) { } BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int c = Integer.parseInt(br.readLine()); String[] sa = br.readLine().split(" "); int a = Integer.parseInt(sa[0]); int b = Integer.parseInt(sa[1]); String s = br.readLine(); br.close(); System.out.println(a + b + c + s); PrintWriter pw = new PrintWriter(System.out); pw.println(123); pw.flush(); pw.close(); // 2^nの全探索 int end = 1 << n; for (int i = 0; i < end; i++) { for (int j = 0; j < n; j++) { if ((i >> j & 1) == 1) { } } } // 二分探索(探索値以上の最小のインデックス) int[] array = new int[5]; int idx = Arrays.binarySearch(array, 5); if (idx < 0) idx = ~idx; // grundy数 // 遷移先のgrundy数の集合に含まれない最小の0以上の整数 } /** * 木の入力を受け取る * N * A1 B1 * ・ * ・ * An-1 Bn-1 * 頂点の番号(0-indexed)<連結頂点リスト> */ static List> inputTree() throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String[] sa = br.readLine().split(" "); int n = Integer.parseInt(sa[0]); List> list = new ArrayList<>(n); for (int i = 0; i < n; i++) { list.add(new ArrayList<>()); } for (int i = 0; i < n - 1; i++) { sa = br.readLine().split(" "); int a = Integer.parseInt(sa[0]) - 1; int b = Integer.parseInt(sa[1]) - 1; list.get(a).add(b); list.get(b).add(a); } br.close(); return list; } /** * 木において、根から近い順の頂点番号のリストを取得する * ※BFSで訪れた頂点の順に追加したリストを作成 * 逆順に参照すれば、根から遠い順に処理を行うことができる */ static List nearRootList() throws Exception { int n = 5; List> list = inputTree(); List near = new ArrayList(n); boolean[] visit = new boolean[n]; Queue que = new ArrayDeque(); near.add(0); que.add(0); visit[0] = true; while (!que.isEmpty()) { Integer cur = que.poll(); for (Integer next : list.get(cur)) { if (!visit[next]) { near.add(next); que.add(next); visit[next] = true; } } } return near; } /** * 二分探索 * * @param array 配列 * @param val 探索値 * @return 探索値以上の最小のインデックス(探索値と同値が複数ある場合は保証なし) */ static int binarySearch(long[] array, long val) { int ok = array.length; int ng = -1; while (Math.abs(ok - ng) > 1) { int mid = (ok + ng) / 2; if (array[mid] >= val) { ok = mid; } else { ng = mid; } } return ok; } /** * 三分探索 * @return */ static double ternarySearch() { double gosa = 1e-8; double l = 0; double r = 1e9; // 計算結果の差にしないと駄目かも while (r - l > gosa) { double m1 = (l * 2 + r) / 3; double m2 = (l + r * 2) / 3; double v1 = m1; // calc(m1) double v2 = m2; if (v1 <= v2) { r = m2; } else { l = m1; } } return r; // calc(r) } /** * 配列を逆順にする */ static void reverse(int[] a) { for (int i = 0; i < a.length / 2; i++) { int tmp = a[i]; a[i] = a[a.length - 1 - i]; a[a.length - 1 - i] = tmp; } } /** * Beanのソート */ static void beanSort() { // vの降順 Hoge[] array = new Hoge[5]; Arrays.sort(array, new Comparator() { public int compare(Hoge o1, Hoge o2) { return o2.v - o1.v; } }); // プライオリティキュー PriorityQueue pq = new PriorityQueue(); pq.add(new Hoge(1, 2)); pq.poll(); // Integer降順 PriorityQueue que2 = new PriorityQueue<>((o1, o2) -> o2 - o1); que2.poll(); } static class Hoge implements Comparable{ int a; int b; int v; public Hoge(int a, int b) { this.a = a; this.b = b; this.v = a + b; } public int compareTo(Hoge o) { return v - o.v; } } /** * 配列をセットやマップのキーにしたい場合 * (カンマ区切りで文字列化とかした場合とどっちが速いかは不明) */ static class ArrayObj { int[] arr; int code = 0; public ArrayObj(int[] a) { arr = new int[a.length]; System.arraycopy(a, 0, arr, 0, a.length); code = Arrays.hashCode(arr); } @Override public boolean equals(Object o) { ArrayObj ao = (ArrayObj) o; for (int i = 0; i < arr.length; i++) { if (arr[i] != ao.arr[i]) { return false; } } return true; } @Override public int hashCode() { return code; } } /** * 階乗とその逆元の事前計算(実行時間約200ms) */ static void kaijou() { int n = 1000001; int mod = 1000000007; long[] p = new long[n]; long[] pi = new long[n]; p[0] = 1; pi[0] = 1; for (int i = 1; i < n; i++) { p[i] = p[i - 1] * i % mod; } pi[n - 1] = BigInteger.valueOf(p[n - 1]) .modInverse(BigInteger.valueOf(mod)).longValue(); for (int i = n - 1; i > 1; i--) { pi[i - 1] = pi[i] * i % mod; } } /** * 二項係数 * 組み合わせの数nCrをmodで割った余りを求められるように階乗を前計算 O(n) * ↓modを取らないもの * https://atcoder.jp/contests/abc011/submissions/11238811 */ static class Kaijou { long[] p, pi; int m; public Kaijou(int n, int mod) { n++; m = mod; p = new long[n]; pi = new long[n]; p[0] = 1; pi[0] = 1; for (int i = 1; i < n; i++) { p[i] = p[i - 1] * i % m; } pi[n - 1] = BigInteger.valueOf(p[n - 1]) .modInverse(BigInteger.valueOf(m)).longValue(); for (int i = n - 1; i > 1; i--) { pi[i - 1] = pi[i] * i % m; } } public long comb(int n, int r) { if (n < r) return 0; return p[n] * pi[r] % m * pi[n - r] % m; } public long perm(int n, int r) { if (n < r) return 0; return p[n] * pi[n - r] % m; } } /** * 組み合わせの数nCr O(r) */ static long nCr(int n, int r) { long val = 1; for (int i = 1; i <= r; i++) { val = val * (n - i + 1) / i; } return val; } /** * 組み合わせの数nCrをmで割った余り O(r) */ static long nCr(int n, int r, int m) { long val = 1; for (int i = 1; i <= r; i++) { val = val * (n - i + 1) % m; val = val * modinv(i, m) % m; } return val; } static long nCr(long n, int r, int m) { long val = 1; for (int i = 1; i <= r; i++) { val = val * ((n - i + 1) % m) % m; val = val * modinv(i, m) % m; } return val; } /** * aのmod mでの逆元 */ static long modinv(long a, int m) { long b = m; long u = 1; long v = 0; long tmp = 0; while (b > 0) { long t = a / b; a -= t * b; tmp = a; a = b; b = tmp; u -= t * v; tmp = u; u = v; v = tmp; } u %= m; if (u < 0) u += m; return u; } /** * パスカルの三角形(実行時間約130ms) */ static int[][] pascal() { int n = 5000; int m = 1000000007; int[][] pas = new int[n + 1][n + 1]; pas[0][0] = 1; for (int i = 0; i < n; i++) { for (int j = 0; j <= i; j++) { pas[i + 1][j] += pas[i][j]; pas[i + 1][j + 1] += pas[i][j]; pas[i + 1][j] %= m; pas[i + 1][j + 1] %= m; } } return pas; } /** * エラトステネスの篩 * 2~nの素因数分解ができるように前計算 */ static class Eratosthenes { int[] div; public Eratosthenes(int n) { if (n < 2) return; div = new int[n + 1]; div[0] = -1; div[1] = -1; int end = (int) Math.sqrt(n) + 1; for (int i = 2; i <= end; i++) { if (div[i] == 0) { div[i] = i; for (int j = i * i; j <= n; j+=i) { if (div[j] == 0) div[j] = i; } } } for (int i = end + 1; i <= n; i++) { if (div[i] == 0) div[i] = i; } } public Map bunkai(int x) { Map soinsu = new HashMap<>(); while (x > 1) { Integer d = div[x]; soinsu.put(d, soinsu.getOrDefault(d, 0) + 1); x /= d; } return soinsu; } public boolean isSosuu(int x) { return div[x] == x; } } /** * 素因数分解 * @param n * @return key:素因数、val:指数 */ static Map bunkai(int n) { Map soinsu = new HashMap<>(); int end = (int) Math.sqrt(n); int d = 2; while (n > 1) { if (n % d == 0) { n /= d; soinsu.put(d, soinsu.getOrDefault(d, 0) + 1); end = (int) Math.sqrt(n); } else { if (d > end) { d = n - 1; } d++; } } return soinsu; } /** * 約数の数 */ static int getDivCnt(int val) { Map soinsu = bunkai(val); int cnt = 1; for (int key : soinsu.keySet()) { cnt *= soinsu.get(key) + 1; } return cnt; } /** * nの約数リスト(実行時間 n=10^16:約860ms) */ static List divList(long n) { List list = new ArrayList<>(); long end = (long) Math.sqrt(n); for (int i = 1; i <= end; i++) { if (n % i == 0) { list.add((long) i); } } int i = end * end == n ? list.size() - 2 : list.size() - 1; for ( ; i >= 0; i--) { list.add(n / list.get(i)); } return list; } static List divList(int n) { List list = new ArrayList<>(); int end = (int) Math.sqrt(n); for (int i = 1; i <= end; i++) { if (n % i == 0) { list.add(i); } } int i = end * end == n ? list.size() - 2 : list.size() - 1; for ( ; i >= 0; i--) { list.add(n / list.get(i)); } return list; } /** * n以下の素数リスト * n=10^6・・・実行時間:約110ms 要素数:78498 * n=10^7・・・実行時間:約1700ms 要素数:664579 */ static List sosuuList(int n) { List sosuu = new ArrayList<>(); for (int i = 2; i <= n; i++) { int r = (int) Math.sqrt(i); boolean flg = false; for (Integer o : sosuu) { if (r < o) { break; } if (i % o == 0) { flg = true; break; } } if (!flg) { sosuu.add(i); } } return sosuu; } /** * 素数判定 */ static boolean isSosuu(long n) { if (n < 2) return false; if (n == 2) return true; long end = (int) Math.sqrt(n) + 1; for (int i = 2; i <= end; i++) { if (n % i == 0) { return false; } } return true; } /** * 最大公約数 */ static long gcd(long a, long b) { return b == 0 ? a : gcd(b, a % b); } /** * 最小公倍数 */ static long lcm(long a, long b) { BigInteger ba = BigInteger.valueOf(a); BigInteger bb = BigInteger.valueOf(b); return ba.multiply(bb).divide(ba.gcd(bb)).longValue(); } /** * a / b mod m */ static long divide(long a, long b, int m) { BigInteger ba = BigInteger.valueOf(a); BigInteger bm = BigInteger.valueOf(m); BigInteger bb = BigInteger.valueOf(b).modInverse(bm); return ba.multiply(bb).mod(bm).longValue(); } /** * xのn乗根(x≦10^18、n≧60だと1) */ static int root(long x, int n) { int ng = 1000000001; int ok = 1; while (ng - ok > 1) { int mid = (ok + ng) / 2; if (Math.pow(mid, n) <= Long.MAX_VALUE) { ok = mid; } else { ng = mid; } } ng = ok + 1; ok = 1; while (ng - ok > 1) { int mid = (ok + ng) / 2; if (power(mid, n) <= x) { ok = mid; } else { ng = mid; } } return ok; } /** * xのn乗 */ static long power(long x, long n) { if (n == 0) { return 1; } long val = power(x, n / 2); val = val * val; if (n % 2 == 1) { val = val * x; } return val; } /** * xのn乗をmで割った余り */ static long power(long x, long n, int m) { if (n == 0) { return 1; } long val = power(x, n / 2, m); val = val * val % m; if (n % 2 == 1) { val = val * x % m; } return val; } /** * 行列累乗 O(N^3logK) * 行列aのk乗の各要素をmで割った余り */ static long[][] matrixPow(long[][] a, long k, int m) { if (k == 1) { return a; } long[][] ret = matrixPow(a, k / 2, m); ret = matrixMul(ret, ret, m); if (k % 2 == 1) { ret = matrixMul(ret, a, m); } return ret; } static long[][] matrixMul(long[][] a, long[][] b, int m) { int n = a.length; long[][] c = new long[n][n]; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { for (int x = 0; x < n; x++) { c[i][j] += a[i][x] * b[x][j]; c[i][j] %= m; } } } return c; } /** * 行列累乗でフィボナッチ数列 */ static void matrixPow() { // 以下のような行列を作ることが多そう // ? ? ? ? a3 // 1 0 0 0 * a2 // 0 1 0 0 a1 // 0 0 1 0 a0 int k = 2; int[][][] c = new int[6][k][k]; c[0][0][0] = 1; c[0][0][1] = 1; c[0][1][0] = 1; for (int i = 0; i < c.length - 1; i++) { for (int j = 0; j < k; j++) { for (int j2 = 0; j2 < k; j2++) { for (int x = 0; x < k; x++) { c[i + 1][j][j2] += c[i][j][x] * c[i][x][j2]; } } } } // a1~a46を出力 for (int m = 0; m < 45; m++) { int[] a = {1, 1}; // a1, a0 for (int i = 0; i < c.length; i++) { if ((m >> i & 1) == 1) { int[] tmp = new int[k]; for (int j = 0; j < k; j++) { for (int x = 0; x < k; x++) { tmp[j] += c[i][j][x] * a[x]; } } a = tmp; } } System.out.println(a[0]); } // トリボナッチ数列 // https://atcoder.jp/contests/abc006/submissions/12291470 // 足し算をXOR、掛け算をANDにしたケース // https://atcoder.jp/contests/abc009/submissions/12290718 } /** * 二次元配列を左90度回転 */ static int[][] turnLeft(int[][] a) { int h = a.length; int w = a[0].length; int w1 = w - 1; int[][] b = new int[w][h]; for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { b[i][j] = a[j][w1 - i]; } } return b; } /** * 二次元配列を右90度回転 */ static int[][] turnRight(int[][] a) { int h = a.length; int w = a[0].length; int h1 = h - 1; int[][] b = new int[w][h]; for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { b[i][j] = a[h1 - j][i]; } } return b; } /** * 掃き出し法 O(NlogA) * 最上位から貪欲に以下のようなものを作る * ※上から3桁目が1の要素がない場合 * arr[0]:1000・・・ ←msb[0]:62 * arr[1]:0100・・・ ←msb[1]:61 * arr[2]:0001・・・ ←msb[2]:59 * rank:3 */ static class Hakidasi { long[] arr; int[] msb; int n, rank; public Hakidasi(long[] a) { n = a.length; arr = Arrays.copyOf(a, n); msb = new int[n]; for (int i = 62; i >= 0; i--) { boolean flg = false; for (int j = rank; j < n; j++) { if ((arr[j] >> i & 1) == 1) { long t = arr[rank]; arr[rank] = arr[j]; arr[j] = t; flg = true; break; } } if (flg) { msb[rank] = i; for (int j = 0; j < n; j++) { if (j != rank && (arr[j] >> i & 1) == 1) { arr[j] ^= arr[rank]; } } rank++; } } } } /** * 値重複可能なHashSet */ static class MultiSet { Map map = new HashMap<>(); int size = 0; void add(T e) { map.put(e, map.getOrDefault(e, 0) + 1); size++; } void remove(T e) { if (e != null && map.containsKey(e)) { int val = map.get(e); if (val == 1) { map.remove(e); } else { map.put(e, val - 1); } size--; } } int sizeAll() { return size; } int sizeUnq() { return map.size(); } boolean contains(T e) { return map.containsKey(e); } boolean isEmpty() { return size == 0; } @SuppressWarnings("unchecked") T[] toArrayUnq() { Object[] arr = map.keySet().toArray(); return (T[]) arr; } @SuppressWarnings("unchecked") T[] toArrayAll() { Object[] arr = new Object[size]; int idx = 0; for (T e : map.keySet()) { int num = map.get(e); for (int i = 0; i < num; i++) { arr[idx] = e; idx++; } } return (T[]) arr; } } /** * 値重複可能なTreeSet */ static class MultiTreeSet { TreeMap map = new TreeMap<>(); int size = 0; void add(T e) { map.put(e, map.getOrDefault(e, 0) + 1); size++; } void remove(T e) { if (e != null && map.containsKey(e)) { int val = map.get(e); if (val == 1) { map.remove(e); } else { map.put(e, val - 1); } size--; } } int sizeAll() { return size; } int sizeUnq() { return map.size(); } boolean contains(T e) { return map.containsKey(e); } boolean isEmpty() { return size == 0; } T lower(T e) { return map.lowerKey(e); } T floor(T e) { return map.floorKey(e); } T higher(T e) { return map.higherKey(e); } T ceiling(T e) { return map.ceilingKey(e); } T first() { return map.firstKey(); } T last() { return map.lastKey(); } T pollFirst() { T e = map.firstKey(); remove(e); return e; } T pollLast() { T e = map.lastKey(); remove(e); return e; } @SuppressWarnings("unchecked") T[] toArrayUnq() { Object[] arr = map.keySet().toArray(); return (T[]) arr; } @SuppressWarnings("unchecked") T[] toArrayAll() { Object[] arr = new Object[size]; int idx = 0; for (T e : map.keySet()) { int num = map.get(e); for (int i = 0; i < num; i++) { arr[idx] = e; idx++; } } return (T[]) arr; } } /** * 対象値の件数をカウント(null回避) * キー:対象値、値:対象値の件数 */ static void addCntMap(Map map, Integer key) { map.put(key, map.getOrDefault(key, 0) + 1); } static void delCntMap(Map map, Integer key) { if (key != null && map.containsKey(key)) { int val = map.get(key); if (val == 1) { map.remove(key); } else { map.put(key, val - 1); } } } /** * UnionFind */ static class UnionFind { int[] parent, size; int num = 0; // 連結成分の数 UnionFind(int n) { parent = new int[n]; size = new int[n]; num = n; for (int i = 0; i < n; i++) { parent[i] = i; size[i] = 1; } } void union(int x, int y) { int px = find(x); int py = find(y); if (px != py) { parent[px] = py; size[py] += size[px]; num--; } } int find(int x) { if (parent[x] == x) { return x; } parent[x] = find(parent[x]); return parent[x]; } /** * xとyが同一連結成分か */ boolean same(int x, int y) { return find(x) == find(y); } /** * xを含む連結成分のサイズ */ int size(int x) { return size[find(x)]; } } // static void union(int[] parent, int[] rank, int x, int y) { // int px = find(parent, x); // int py = find(parent, y); // if (px != py) { // if (rank[px] < rank[py]) { // parent[px] = py; // } else { // parent[py] = px; // if (rank[px] == rank[py]) { // rank[px]++; // } // } // } // } /** * ワーシャルフロイド法 O(N^3) */ static void warshallFloyd() { // i=jは0、辺があればその重み、それ以外は無限大で初期化 int n = 5; int m = 10; int inf = 1000000000; int[][] d = new int[n][n]; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (i != j) { d[i][j] = inf; } } } for (int i = 0; i < m; i++) { int a = 0; int b = 1; int t = 100; d[a][b] = t; d[b][a] = t; } for (int k = 0; k < n; k++) { for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (d[i][k] != inf && d[k][j] != inf) { d[i][j] = Math.min(d[i][j], d[i][k] + d[k][j]); } } } } } /** * ベルマンフォード法 O(NM) */ static long[] bellmanFord() throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String[] sa = br.readLine().split(" "); int n = Integer.parseInt(sa[0]); int m = Integer.parseInt(sa[1]); int[] a = new int[m]; int[] b = new int[m]; int[] c = new int[m]; for (int i = 0; i < m; i++) { sa = br.readLine().split(" "); a[i] = Integer.parseInt(sa[0]) - 1; b[i] = Integer.parseInt(sa[1]) - 1; c[i] = Integer.parseInt(sa[2]); } br.close(); // 関数化したい場合 // static long[] bellmanFord(int[] a, int[] b, int[] c, int s) { // int n = a.length; int s = 0; long[] d = new long[n]; Arrays.fill(d, 1_000_000_000_000_000_000L); d[s] = 0; boolean upd = true; for (int i = 0; i <= n && upd; i++) { upd = false; for (int j = 0; j < m; j++) { long alt = d[a[j]] + c[j]; if (alt < d[b[j]]) { d[b[j]] = alt; upd = true; } } if (i == n) { // 負閉路時処理 } } return d; } /** * ダイクストラ法 O(M + NlogN) */ static long[] dijkstra() throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String[] sa = br.readLine().split(" "); int n = Integer.parseInt(sa[0]); int m = Integer.parseInt(sa[1]); List> list = new ArrayList<>(n); for (int i = 0; i < n; i++) { list.add(new ArrayList<>()); } for (int i = 0; i < m; i++) { sa = br.readLine().split(" "); int a = Integer.parseInt(sa[0]) - 1; int b = Integer.parseInt(sa[1]) - 1; int c = Integer.parseInt(sa[2]); list.get(a).add(new Hen(b, c)); list.get(b).add(new Hen(a, c)); } br.close(); // 関数化したい場合 // static long[] dijkstra(List> list, int s) { int s = 0; long[] d = new long[list.size()]; Arrays.fill(d, Long.MAX_VALUE); d[s] = 0; PriorityQueue que = new PriorityQueue(); Node first = new Node(s, 0); que.add(first); while (!que.isEmpty()) { Node cur = que.poll(); // 要見直し // 辺が多すぎる場合、同じcurを二度見ない条件がないとTLEするっぽい // 全組合せの辺があるなら、NextSetを作り、Curを取り除けたら次を調べる // PriorityQueueではなくTreeSetを使うなら、removeしてからaddする // ↓怪しいけど暫定。確定済み配列を持つのが確実か。 if (cur.d > d[cur.v]) { continue; } for (Hen hen : list.get(cur.v)) { long alt = d[cur.v] + hen.c; if (alt < d[hen.v]) { d[hen.v] = alt; que.add(new Node(hen.v, alt)); } } } return d; } static class Hen { int v, c; public Hen(int v, int c) { this.v = v; this.c = c; } } static class Node implements Comparable { int v; long d; public Node(int v, long d) { this.v = v; this.d = d; } public int compareTo(Node o) { return Long.compare(d, o.d); } } /** * トポロジカルソート O(N + M) */ static List topologicalSort() throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String[] sa = br.readLine().split(" "); int n = Integer.parseInt(sa[0]); int m = Integer.parseInt(sa[1]); List> list = new ArrayList<>(n); for (int i = 0; i < n; i++) { list.add(new ArrayList<>()); } int[] inCnt = new int[n]; for (int i = 0; i < m; i++) { sa = br.readLine().split(" "); int a = Integer.parseInt(sa[0]) - 1; int b = Integer.parseInt(sa[1]) - 1; list.get(a).add(b); inCnt[b]++; } br.close(); List res = new ArrayList<>(n); Queue que = new ArrayDeque<>(); for (int i = 0; i < n; i++) { if (inCnt[i] == 0) { que.add(i); } } while (!que.isEmpty()) { int cur = que.poll(); res.add(cur); for (int i : list.get(cur)) { inCnt[i]--; if (inCnt[i] == 0) { que.add(i); } } } return res; } /** * 二部グラフの構築(Node2.grpに1か2を設定する) */ static Node2[] nibuGraph() throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String[] sa = br.readLine().split(" "); int n = Integer.parseInt(sa[0]); int m = Integer.parseInt(sa[1]); Node2[] arr = new Node2[n]; for (int i = 0; i < n; i++) { Node2 o = new Node2(); arr[i] = o; } for (int i = 0; i < m; i++) { sa = br.readLine().split(" "); int a = Integer.parseInt(sa[0]) - 1; int b = Integer.parseInt(sa[1]) - 1; arr[a].nexts.add(b); arr[b].nexts.add(a); } br.close(); Queue que = new ArrayDeque<>(); que.add(0); arr[0].grp = 1; while (!que.isEmpty()) { int cur = que.poll(); for (int next : arr[cur].nexts) { if (arr[next].grp == 0) { que.add(next); arr[next].grp = 3 - arr[cur].grp; } else if (arr[next].grp == arr[cur].grp) { // 二部グラフではない } } } // 最大マッチング int cntmatch = 0; for (int i = 0; i < n; i++) { if (arr[i].grp == 1 && arr[i].target == null && matching(arr, new HashSet<>(), i)) { cntmatch++; } } System.out.println(cntmatch); return arr; } /** * (二部グラフの最大マッチング)増加路の探索、マッチング更新 */ static boolean matching(Node2[] arr, Set used, int cur) { Node2 o = arr[cur]; for (int next : o.nexts) { if (!used.contains(next)) { used.add(next); if (arr[next].target == null || matching(arr, used, arr[next].target)) { arr[next].target = cur; arr[cur].target = next; return true; } } } return false; } static class Node2 { int grp; Integer target; List nexts = new ArrayList<>(); } /** * 最大流、最小カット(フォード・ファルカーソンのアルゴリズム) O(E・maxflow) */ static class MaxFlow { int n; List> list; boolean[] visit; public MaxFlow(int n) { this.n = n; list = new ArrayList<>(n); for (int i = 0; i < n; i++) { list.add(new ArrayList<>()); } visit = new boolean[n]; } void addHen(int u, int v, int cap) { list.get(u).add(new Hen(v, list.get(v).size(), cap)); list.get(v).add(new Hen(u, list.get(u).size() - 1, cap)); } int calc(int start, int goal) { int ret = 0; while (true) { Arrays.fill(visit, false); int used = dfs(start, goal, Integer.MAX_VALUE); if (used == 0) { return ret; } ret += used; } } private int dfs(int cur, int goal, int src) { if (cur == goal) { return src; } visit[cur] = true; for (Hen h : list.get(cur)) { if (!visit[h.v] && h.cap > 0) { int used = dfs(h.v, goal, Math.min(src, h.cap)); if (used > 0) { h.cap -= used; list.get(h.v).get(h.vi).cap += used; return used; } } } return 0; } private class Hen { int v, vi, cap; public Hen(int v, int vi, int cap) { this.v = v; this.vi = vi; this.cap = cap; } } } static void rerooting2() throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String[] sa = br.readLine().split(" "); int n = Integer.parseInt(sa[0]); int m = Integer.parseInt(sa[1]); int[][] edges = new int[n - 1][2]; for (int i = 0; i < edges.length; i++) { sa = br.readLine().split(" "); edges[i][0] = Integer.parseInt(sa[0]) - 1; edges[i][1] = Integer.parseInt(sa[1]) - 1; } br.close(); Rerooting2 r = new Rerooting2<>(n, edges, 1L, (o1, o2) -> { return o1 * o2 % m; }, (o, i) -> { return o + 1; }); PrintWriter pw = new PrintWriter(System.out); for (int i = 0; i < n; i++) { pw.println(r.query(i) - 1); } pw.flush(); } /** * 全方位木DP */ static class Rerooting2 { public int nodeCnt; private T identity; private BiFunction merge; private BiFunction addNode; private List> adjacents; private List> indexForAdjacent; private Object[][] dp; private Object[] res; /** * 初期化 * @param nodeCnt 頂点数(≧1) * @param edges 木構造の辺の配列 例:{{0,1},{0,2},{1,3}} * @param identity 単位元 * @param merge 部分木のマージ関数(戻り値は新規オブジェクト) * @param addNode 指定頂点(部分木の根)の分を加算する関数(戻り値は新規オブジェクト) */ public Rerooting2(int nodeCnt, int[][] edges, T identity, BiFunction merge, BiFunction addNode) { this.nodeCnt = nodeCnt; this.identity = identity; this.merge = merge; this.addNode = addNode; adjacents = new ArrayList<>(nodeCnt); indexForAdjacent = new ArrayList<>(nodeCnt); for (int i = 0; i < nodeCnt; i++) { adjacents.add(new ArrayList<>()); indexForAdjacent.add(new ArrayList<>()); } for (int i = 0; i < edges.length; i++) { int[] edge = edges[i]; indexForAdjacent.get(edge[0]).add(adjacents.get(edge[1]).size()); indexForAdjacent.get(edge[1]).add(adjacents.get(edge[0]).size()); adjacents.get(edge[0]).add(edge[1]); adjacents.get(edge[1]).add(edge[0]); } dp = new Object[nodeCnt][]; res = new Object[nodeCnt]; for (int i = 0; i < nodeCnt; i++) { dp[i] = new Object[adjacents.get(i).size()]; } if (nodeCnt == 1) { res[0] = addNode.apply(identity, 0); } else { initialize(); } } @SuppressWarnings("unchecked") public T query(int node) { return (T) res[node]; } @SuppressWarnings("unchecked") private void initialize() { int[] parents = new int[nodeCnt]; int[] order = new int[nodeCnt]; // InitOrderedTree int index = 0; Deque stack = new ArrayDeque<>(); stack.addFirst(0); parents[0] = -1; while (!stack.isEmpty()) { int current = stack.pollFirst(); order[index++] = current; for (int adjacent : adjacents.get(current)) { if (adjacent == parents[current]) continue; stack.addFirst(adjacent); parents[adjacent] = current; } } // FromLeaf for (int i = order.length - 1; i >= 1; i--) { int node = order[i]; int parent = parents[node]; T accum = identity; int parentIndex = -1; for (int j = 0; j < adjacents.get(node).size(); j++) { if (adjacents.get(node).get(j) == parent) { parentIndex = j; continue; } accum = merge.apply(accum, (T) dp[node][j]); } dp[parent][indexForAdjacent.get(node).get(parentIndex)] = addNode.apply(accum, node); } // ToLeaf for (int node : order) { T accum = identity; Object[] accumsFromTail = new Object[adjacents.get(node).size()]; accumsFromTail[accumsFromTail.length - 1] = identity; for (int j = accumsFromTail.length - 1; j >= 1; j--) { accumsFromTail[j - 1] = merge.apply((T) dp[node][j], (T) accumsFromTail[j]); } for (int j = 0; j < accumsFromTail.length; j++) { dp[adjacents.get(node).get(j)][indexForAdjacent.get(node).get(j)] = addNode.apply(merge.apply(accum, (T) accumsFromTail[j]), node); accum = merge.apply(accum, (T) dp[node][j]); } res[node] = addNode.apply(accum, node); } } } static void rerooting3() throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String[] sa = br.readLine().split(" "); int n = Integer.parseInt(sa[0]); int m = Integer.parseInt(sa[1]); int[][] edges = new int[n - 1][2]; for (int i = 0; i < edges.length; i++) { sa = br.readLine().split(" "); edges[i][0] = Integer.parseInt(sa[0]) - 1; edges[i][1] = Integer.parseInt(sa[1]) - 1; } br.close(); Rerooting3 r = new Rerooting3<>(n, edges, 1L, (o, i) -> { return o + 1; }, (o1, o2) -> { return o1 * o2 % m; }, (o, i) -> { return o; }); // Rerooting3 r = new Rerooting3<>(n, edges, new Obj(), // (o, i) -> { // return o; // }, // (o1, o2) -> { // Obj ret = new Obj(); // ret.max = Math.max(o1.isAcm ? o1.max : o1.size, o2.isAcm ? o2.max : o2.size); // ret.size = o1.size + o2.size; // ret.isAcm = true; // return ret; // }, // (o, i) -> { // Obj ret = new Obj(); // ret.max = o.max; // ret.size = o.size + 1; // return ret; // }); PrintWriter pw = new PrintWriter(System.out); for (int i = 0; i < n; i++) { pw.println(r.query(i)); } for (int i = 0; i < n; i++) { int max = 0; for (int j = 0; j < r.dp[i].length; j++) { max = Math.max(max, (int) r.dp[i][j]); } pw.println(max); } pw.flush(); } /** * 全方位木DP */ static class Rerooting3 { public int nodeCnt; private T identity; private BiFunction beforeMerge; private BiFunction merge; private BiFunction afterMerge; private List> adjacents; private List> indexForAdjacent; private Object[][] dp; private Object[] res; /** * 初期化 * @param nodeCnt 頂点数(≧1) * @param edges 木構造の辺の配列 例:{{0,1},{0,2},{1,3}} * @param identity 単位元 * @param beforeMerge 指定頂点(部分木の子)を計算する関数(何もしない場合以外戻り値は新規オブジェクト) * @param merge 部分木のマージ関数(戻り値は新規オブジェクト) * @param afterMerge 指定頂点(部分木の根)の分を計算する関数(何もしない場合以外戻り値は新規オブジェクト) */ public Rerooting3(int nodeCnt, int[][] edges, T identity, BiFunction beforeMerge, BiFunction merge, BiFunction afterMerge) { this.nodeCnt = nodeCnt; this.identity = identity; this.beforeMerge = beforeMerge; this.merge = merge; this.afterMerge = afterMerge; adjacents = new ArrayList<>(nodeCnt); indexForAdjacent = new ArrayList<>(nodeCnt); for (int i = 0; i < nodeCnt; i++) { adjacents.add(new ArrayList<>()); indexForAdjacent.add(new ArrayList<>()); } for (int i = 0; i < edges.length; i++) { int[] edge = edges[i]; indexForAdjacent.get(edge[0]).add(adjacents.get(edge[1]).size()); indexForAdjacent.get(edge[1]).add(adjacents.get(edge[0]).size()); adjacents.get(edge[0]).add(edge[1]); adjacents.get(edge[1]).add(edge[0]); } dp = new Object[nodeCnt][]; res = new Object[nodeCnt]; for (int i = 0; i < nodeCnt; i++) { dp[i] = new Object[adjacents.get(i).size()]; } if (nodeCnt == 1) { res[0] = afterMerge.apply(identity, 0); } else { initialize(); } } @SuppressWarnings("unchecked") public T query(int node) { return (T) res[node]; } @SuppressWarnings("unchecked") private void initialize() { int[] parents = new int[nodeCnt]; int[] order = new int[nodeCnt]; // InitOrderedTree int index = 0; Deque stack = new ArrayDeque<>(); stack.addFirst(0); parents[0] = -1; while (!stack.isEmpty()) { int current = stack.pollFirst(); order[index++] = current; for (int adjacent : adjacents.get(current)) { if (adjacent == parents[current]) continue; stack.addFirst(adjacent); parents[adjacent] = current; } } // FromLeaf for (int i = order.length - 1; i >= 1; i--) { int node = order[i]; int parent = parents[node]; T accum = identity; int parentIndex = -1; for (int j = 0; j < adjacents.get(node).size(); j++) { if (adjacents.get(node).get(j) == parent) { parentIndex = j; continue; } accum = merge.apply(accum, beforeMerge.apply((T) dp[node][j], adjacents.get(node).get(j))); } dp[parent][indexForAdjacent.get(node).get(parentIndex)] = afterMerge.apply(accum, node); } // ToLeaf for (int node : order) { T accum = identity; Object[] accumsFromTail = new Object[adjacents.get(node).size()]; accumsFromTail[accumsFromTail.length - 1] = identity; for (int j = accumsFromTail.length - 1; j >= 1; j--) { accumsFromTail[j - 1] = merge.apply( (T) accumsFromTail[j], beforeMerge.apply((T) dp[node][j], adjacents.get(node).get(j))); } for (int j = 0; j < accumsFromTail.length; j++) { dp[adjacents.get(node).get(j)][indexForAdjacent.get(node).get(j)] = afterMerge.apply(merge.apply(accum, (T) accumsFromTail[j]), node); accum = merge.apply(accum, beforeMerge.apply((T) dp[node][j], adjacents.get(node).get(j))); } res[node] = afterMerge.apply(accum, node); } } } /** * 強連結成分分解 */ static class StrongBunkai { private List> list1, list2; private boolean[] visit; private List order; public int cnt = 0; // 強連結成分の数 public int[] grp; // グループ(配列の要素数:n、値:0~cnt-1) public int[] size; // グループ(0~cnt-1)の頂点数 public List> list; // 強連結成分間のみの隣接リスト /** * 初期化 * @param n 頂点数 * @param list1 隣接リスト * @param list2 逆辺の隣接リスト */ public StrongBunkai(int n, List> list1, List> list2) { this.list1 = list1; this.list2 = list2; visit = new boolean[n]; order = new ArrayList<>(n); grp = new int[n]; Arrays.fill(grp, -1); for (int i = 0; i < n; i++) { dfs(i); } Collections.reverse(order); for (int i : order) { if (grp[i] == -1) { dfsr(i); cnt++; } } size = new int[cnt]; list = new ArrayList<>(cnt); for (int i = 0; i < cnt; i++) { list.add(new HashSet<>()); } for (int i = 0; i < n; i++) { size[grp[i]]++; for (int j : list1.get(i)) { if (grp[i] != grp[j]) { list.get(grp[i]).add(grp[j]); } } } } private void dfs(int x) { if (visit[x]) { return; } visit[x] = true; for (int y : list1.get(x)) { dfs(y); } order.add(x); } private void dfsr(int x) { grp[x] = cnt; for (int y : list2.get(x)) { if (grp[y] == -1) { dfsr(y); } } } } /** * 幅優先探索 */ static void bfs() { Queue que = new ArrayDeque<>(); que.add(0); // firstに訪問済みの印を付ける while (!que.isEmpty()) { Integer cur = que.poll(); // ↓次に遷移可能な数分ループ for (int i = 0; i < 4; i++) { // ↓未訪問の場合 if (cur == null) { que.add(i); // nextに訪問済みの印を付ける } } } } /** * 配列要素の順列の全列挙(N=10で実行時間約500ms) * setは配列のインデックスを格納するワーク変数 * permutation(target, new LinkedHashSet<>()); */ static void permutation(int[] target, LinkedHashSet set) { if (set.size() == target.length) { // for (int i : set) { // // target[i]に関する処理 // } return; } for (int i = 0; i < target.length; i++) { if (!set.contains(i)) { set.add(i); permutation(target, set); set.remove(i); } } } /** * 配列からk個の要素を取る組み合わせの全列挙 * listは配列のインデックスを格納するワーク変数 * combination(target, k, new ArrayList()); */ static void combination(int[] target, int k, List list) { if (list.size() == k) { // for (int i : list) { // // target[i]に関する処理 // } return; } int i = 0; if (!list.isEmpty()) { i = list.get(list.size() - 1) + 1; if (list.size() + target.length - i < k) { return; } } for ( ; i < target.length; i++) { list.add(i); combination(target, k, list); list.remove(list.size() - 1); } } /** * 順列を辞書順で1つ進める O(NlogN) * 降順ソートされている場合はnull * ※前提:N≧2、要素が相異なる */ static int[] nextPermutation(int[] a) { TreeSet set = new TreeSet<>(); set.add(a[a.length - 1]); for (int i = a.length - 2; i >= 0; i--) { if (a[i] > a[i + 1] || set.higher(a[i]) == null) { set.add(a[i]); } else { int[] b = new int[a.length]; for (int j = 0; j < i; j++) { b[j] = a[j]; } b[i] = set.higher(a[i]); set.remove(b[i]); set.add(a[i]); Integer[] arr = set.toArray(new Integer[0]); for (int j = 0; j < arr.length; j++) { b[i + 1 + j] = arr[j]; } return b; } } return null; } /** * 最長増加部分列(Longest Increasing Subsequence)※狭義単調増加 * @return 列の長さ */ static int lis() { int n = 5; int[] a = {1, 4, 2, 3, 5}; // 長さiの部分列の最後の数値として最小のものを持つ配列 int[] val = new int[n + 1]; for (int i = 1; i <= n; i++) { val[i] = Integer.MAX_VALUE; } for (int i = 0; i < n; i++) { int idx = Arrays.binarySearch(val, a[i]); // // 広義単調増加の場合 // int idx = Arrays.binarySearch(val, a[i] + 1); if (idx < 0) idx = ~idx; val[idx] = a[i]; } int lis = Arrays.binarySearch(val, Integer.MAX_VALUE - 1); if (lis < 0) { lis = ~lis; lis--; } return lis; } /** * 最長共通部分列 */ static char[] lcs(char[] s, char[] t) { int a = s.length; int b = t.length; int[][] dp = new int[a + 1][b + 1]; for (int i = 1; i <= a; i++) { for (int j = 1; j <= b; j++) { if (s[i - 1] == t[j - 1]) { dp[i][j] = dp[i - 1][j - 1] + 1; } else { dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); } } } char[] lcs = new char[dp[a][b]]; while (a > 0 && b > 0) { if (dp[a][b] == dp[a - 1][b]) { a--; } else if (dp[a][b] == dp[a][b - 1]) { b--; } else { a--; b--; lcs[dp[a][b]] = s[a]; } } return lcs; } /** * MP法 * arr2にarr1が出現するインデックス(0~|arr2|-|arr1|)を全て取得 * arrをint→charにすれば文字列にも使えるはず・・ */ static class MP { int n; int[] arr; int[] a; public MP(int[] arr1) { arr = arr1; n = arr.length; a = new int[n + 1]; a[0] = -1; int j = -1; for (int i = 0; i < n; i++) { while (j >= 0 && arr[i] != arr[j]) { j = a[j]; } j++; a[i + 1] = j; } } List findAllFrom(int[] arr2) { List ret = new ArrayList<>(); int j = 0; for (int i = 0; i < arr2.length; i++) { while (j >= 0 && arr2[i] != arr[j]) { j = a[j]; } j++; if (j == n) { ret.add(i - j + 1); j = a[j]; } } return ret; } } /** * ナップサック問題(選択したものSetに復元) */ static void knapsack() { // https://atcoder.jp/contests/abc145/submissions/8501047 } /** * 座標圧縮 * {2, -1, 5, -3} → {3, 2, 4, 1} */ static int[] zaatu(long[] a) { TreeMap map = new TreeMap<>(); for (int i = 0; i < a.length; i++) { map.put(a[i], null); } Long[] arr = map.keySet().toArray(new Long[0]); int cnt = 0; for (Long i : arr) { cnt++; map.put(i, cnt); } int[] b = new int[a.length]; for (int i = 0; i < a.length; i++) { b[i] = map.get(a[i]); } return b; } /** * 高速ゼータ変換 O(N・2^N) */ static void fastZetaHenkan() { int n = 20; int n2 = 1 << n; int[] dp = new int[n2]; for (int j = 0; j < n; j++) { for (int i = 0; i < n2; i++) { if ((i >> j & 1) == 1) { dp[i] += dp[i & ~(1 << j)]; } } } } /** * トライ木 */ static void trie() { // https://atcoder.jp/contests/agc047/submissions/15830815 } /** * 転倒数 * 0<a[i]であれば前半は不要 * a[i]が大きいなら要座圧 */ static long tentousuu(int[] a) { int n = a.length; int min = Integer.MAX_VALUE; int max = Integer.MIN_VALUE; for (int i = 0; i < n; i++) { min = Math.min(min, a[i]); max = Math.max(max, a[i]); } min--; int[] b = new int[n]; for (int i = 0; i < n; i++) { b[i] = a[i] - min; } BIT bit = new BIT(max - min); long ret = 0; for (int i = 0; i < n; i++) { ret += i - bit.sum(b[i]); bit.add(b[i], 1); } return ret; } /** * Binary Indexed Tree * 要素数nで初期化 * add、sumは1-indexed */ static class BIT { int n; long[] arr; public BIT(int n) { this.n = n; arr = new long[n + 1]; } void add(int idx, long val) { for (int i = idx; i <= n; i += i & -i) { arr[i] += val; } } long sum(int idx) { long sum = 0; for (int i = idx; i > 0; i -= i & -i) { sum += arr[i]; } return sum; } } /** * セグ木で最小共通祖先(Lowest Common Ancestor) */ static class SegTreeLCA { int n = 2; int n2, idx; Node[] arr; int[] first; Node INF = new Node(-1, Integer.MAX_VALUE); /** * 初期化 * @param size 頂点数 * @param list 隣接リスト * @param root 根の頂点番号 */ public SegTreeLCA(int size, List> list, int root) { while (n < size) { n <<= 1; } n <<= 1; n2 = n * 2 - 1; idx = n - 1; arr = new Node[n2]; first = new int[size]; Arrays.fill(first, -1); Node r = new Node(root, 0); arr[idx] = r; first[root] = idx - n + 1; idx++; dfs(list, r, -1); for (int i = idx; i < n2; i++) { arr[i] = INF; } for (int i = n - 2; i >= 0; i--) { if (arr[i * 2 + 1].dep < arr[i * 2 + 2].dep) { arr[i] = arr[i * 2 + 1]; } else { arr[i] = arr[i * 2 + 2]; } } } private void dfs(List> list, Node cur, int p) { for (int c : list.get(cur.i)) { if (c != p) { Node o = new Node(c, cur.dep + 1); arr[idx] = o; if (first[c] == -1) { first[c] = idx - n + 1; } idx++; dfs(list, o, cur.i); arr[idx] = cur; idx++; } } } /** * 頂点Iのオブジェクト取得(0≦I<size) */ public Node get(int i) { return arr[first[i] + n - 1]; } /** * 頂点A,BのLCA取得(0≦A,B<size) */ public Node lca(int a, int b) { int l = Math.min(first[a], first[b]); int r = Math.max(first[a], first[b]); return query(l, r + 1, 0, 0, n); } private Node query(int l, int r, int k, int beg, int end) { if (end <= l || r <= beg) return INF; // 交差しない if (l <= beg && end <= r) return arr[k]; // 完全に含む Node o1 = query(l, r, k * 2 + 1, beg, (beg + end) / 2); Node o2 = query(l, r, k * 2 + 2, (beg + end) / 2, end); if (o1.dep < o2.dep) { return o1; } return o2; } private static class Node { int i, dep; public Node(int i, int dep) { this.i = i; this.dep = dep; } } } /** * ダブリングで最小共通祖先(Lowest Common Ancestor) */ static class DoublingLCA { int n, h = 1, root; List> list; Node[] nodes; int[][] parent; /** * 初期化 * @param list 隣接リスト * @param root 根の頂点番号 */ public DoublingLCA(List> list, int root) { n = list.size(); this.root = root; this.list = list; while ((1 << h) < n) { h++; } nodes = new Node[n]; for (int i = 0; i < n; i++) { nodes[i] = new Node(i, -1); } parent = new int[h][n]; for (int i = 0; i < h; i++) { Arrays.fill(parent[i], -1); } dfs(root, -1, 0); for (int i = 0; i < h - 1; i++) { for (int j = 0; j < n; j++) { if (parent[i][j] != -1) { parent[i + 1][j] = parent[i][parent[i][j]]; } } } } private void dfs(int x, int p, int dep) { parent[0][x] = p; nodes[x].dep = dep; for (int next : list.get(x)) { if (next != p) { dfs(next, x, dep + 1); } } } /** * 頂点A,BのLCA取得(0≦A,B<N) */ public Node lca(int a, int b) { int u = a; int v = b; if (nodes[u].dep < nodes[v].dep) { u = b; v = a; } for (int i = 0; i < h; i++) { if ((nodes[u].dep - nodes[v].dep >> i & 1) == 1) { u = parent[i][u]; } } if (u == v) { return nodes[u]; } for (int i = h - 1; i >= 0; i--) { if (parent[i][u] != parent[i][v]) { u = parent[i][u]; v = parent[i][v]; } } return nodes[parent[0][u]]; } public static class Node { int i, dep; public Node(int i, int dep) { this.i = i; this.dep = dep; } } } static void doublingTree() throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String[] sa = br.readLine().split(" "); int n = Integer.parseInt(sa[0]); List> list = new ArrayList<>(n); for (int i = 0; i < n; i++) { list.add(new ArrayList<>()); } for (int i = 0; i < n - 1; i++) { sa = br.readLine().split(" "); int a = Integer.parseInt(sa[0]) - 1; int b = Integer.parseInt(sa[1]) - 1; int c = Integer.parseInt(sa[2]); DoublingTree.Hen h = new DoublingTree.Hen(i, a, b, c); list.get(h.u).add(h); list.get(h.v).add(h); } br.close(); DoublingTree dt = new DoublingTree(list, 0); System.out.println(dt.maxCost(0, 1)); } /** * ダブリング(木上クエリ処理用) */ static class DoublingTree { int n, h = 1, root; List> list; Node[] nodes; int[][] parent; int[][] maxCost; public DoublingTree(List> list, int root) { n = list.size(); this.root = root; this.list = list; while ((1 << h) < n) { h++; } nodes = new Node[n]; for (int i = 0; i < n; i++) { nodes[i] = new Node(i, -1); } parent = new int[h][n]; for (int i = 0; i < h; i++) { Arrays.fill(parent[i], -1); } maxCost = new int[h][n]; dfs(root, -1, 0); for (int i = 0; i < h - 1; i++) { for (int j = 0; j < n; j++) { if (parent[i][j] != -1) { parent[i + 1][j] = parent[i][parent[i][j]]; maxCost[i + 1][j] = Math.max(maxCost[i][j], maxCost[i][parent[i][j]]); } else { maxCost[i + 1][j] = maxCost[i][j]; } } } } private void dfs(int x, int p, int dep) { parent[0][x] = p; nodes[x].dep = dep; for (Hen h : list.get(x)) { int next = h.u; if (next == x) { next = h.v; } if (next != p) { dfs(next, x, dep + 1); } else { maxCost[0][x] = h.cost; } } } // public Node lca(int a, int b) { // int u = a; // int v = b; // if (nodes[u].dep < nodes[v].dep) { // u = b; // v = a; // } // for (int i = 0; i < h; i++) { // if ((nodes[u].dep - nodes[v].dep >> i & 1) == 1) { // u = parent[i][u]; // } // } // if (u == v) { // return nodes[u]; // } // for (int i = h - 1; i >= 0; i--) { // if (parent[i][u] != parent[i][v]) { // u = parent[i][u]; // v = parent[i][v]; // } // } // return nodes[parent[0][u]]; // } /** * 頂点A,B間の辺の最大コストを取得(0≦A,B<size) */ public int maxCost(int a, int b) { int u = a; int v = b; if (nodes[u].dep < nodes[v].dep) { u = b; v = a; } int ret = 0; for (int i = 0; i < h; i++) { if ((nodes[u].dep - nodes[v].dep >> i & 1) == 1) { ret = Math.max(ret, maxCost[i][u]); u = parent[i][u]; } } if (u == v) { return ret; } for (int i = h - 1; i >= 0; i--) { if (parent[i][u] != parent[i][v]) { ret = Math.max(ret, maxCost[i][u]); ret = Math.max(ret, maxCost[i][v]); u = parent[i][u]; v = parent[i][v]; } } ret = Math.max(ret, maxCost[0][u]); ret = Math.max(ret, maxCost[0][v]); return ret; } public static class Node { int i, dep; public Node(int i, int dep) { this.i = i; this.dep = dep; } } public static class Hen { int i, u, v, cost; long ans; public Hen(int i, int u, int v, int cost) { this.i = i; this.u = u; this.v = v; this.cost = cost; } } } /** * セグ木 * ■最大 * SegTree st = new SegTree<>(n, 0, (v1, v2) -> Math.max(v1, v2)); * ■最小 * SegTree st = new SegTree<>(n, Integer.MAX_VALUE, (v1, v2) -> Math.min(v1, v2)); * ■和 * SegTree st = new SegTree<>(n, 0, (v1, v2) -> v1 + v2); */ static class SegTree { // 親へのアクセス:(n - 1) / 2 // 子へのアクセス:n * 2 + 1、n * 2 + 2 int n = 2; // 要素(葉)の数 int n2; // 全ノード数 T NA; List list; BiFunction func; /** * 初期化 O(N) * @param num 要素(葉)の数 * @param na 単位元 * @param func 区間に対する操作が可能な関数(戻り値は新規オブジェクト) */ public SegTree(int num, T na, BiFunction func) { while (n < num) n <<= 1; n2 = n * 2 - 1; NA = na; list = new ArrayList(n2); for (int i = 0; i < n2; i++) list.add(NA); this.func = func; } /** * 更新 O(logN) * @param i インデックス(0~n-1) * @param x 更新値 */ void update(int i, T x) { i += n - 1; // arr上でのインデックス list.set(i, x); while (i > 0) { i = (i - 1) / 2; list.set(i, func.apply(list.get(i * 2 + 1), list.get(i * 2 + 2))); } } /** * 取得 O(logN) * インデックスl以上r未満の区間の値 */ T query(int l, int r) { return query(l, r, 0, 0, n); } /** * 取得 O(logN) * インデックスl以上r未満の区間の値、beg, endにはノードkに対応する区間 * query(l, r, 0, 0, n); */ private T query(int l, int r, int k, int beg, int end) { if (end <= l || r <= beg) return NA; // 交差しない if (l <= beg && end <= r) return list.get(k); // 完全に含む T v1 = query(l, r, k * 2 + 1, beg, (beg + end) / 2); T v2 = query(l, r, k * 2 + 2, (beg + end) / 2, end); return func.apply(v1, v2); } } }