結果
| 問題 |
No.1418 Sum of Sum of Subtree Size
|
| コンテスト | |
| ユーザー |
ks2m
|
| 提出日時 | 2021-03-05 23:30:08 |
| 言語 | Java (openjdk 23) |
| 結果 |
AC
|
| 実行時間 | 1,208 ms / 2,000 ms |
| コード長 | 5,101 bytes |
| コンパイル時間 | 3,203 ms |
| コンパイル使用メモリ | 90,272 KB |
| 実行使用メモリ | 98,180 KB |
| 最終ジャッジ日時 | 2024-10-07 05:27:10 |
| 合計ジャッジ時間 | 23,975 ms |
|
ジャッジサーバーID (参考情報) |
judge2 / judge3 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 3 |
| other | AC * 41 |
ソースコード
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.function.BiFunction;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
int[][] edges = new int[n - 1][2];
for (int i = 0; i < n - 1; i++) {
String[] sa = br.readLine().split(" ");
edges[i][0] = Integer.parseInt(sa[0]) - 1;
edges[i][1] = Integer.parseInt(sa[1]) - 1;
}
br.close();
Rerooting3<Obj> r = new Rerooting3<>(n, edges, new Obj(),
(o, i) -> {
Obj ret = new Obj();
ret.s = o.s;
ret.v = o.v;
return ret;
},
(o1, o2) -> {
Obj ret = new Obj();
ret.s = o1.s + o2.s;
ret.v = o1.v + o2.v;
return ret;
},
(o, i) -> {
Obj ret = new Obj();
ret.s = o.s + 1;
ret.v = o.v + ret.s;
return ret;
});
long ans = 0;
for (int i = 0; i < n; i++) {
ans += r.query(i).v;
}
System.out.println(ans);
}
static class Obj {
int s;
long v;
}
static class Rerooting3<T> {
public int nodeCnt;
private T identity;
private BiFunction<T, Integer, T> beforeMerge;
private BiFunction<T, T, T> merge;
private BiFunction<T, Integer, T> afterMerge;
private List<List<Integer>> adjacents;
private List<List<Integer>> 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<T, Integer, T> beforeMerge,
BiFunction<T, T, T> merge, BiFunction<T, Integer, T> 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<Integer> 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);
}
}
}
}
ks2m