あるエンジニアのAtCoder奮闘記

東京都港区にあるアミフィアブル株式会社のエンジニアが、AtCoderで解いた問題について振り返ったりしていく会社公認のブログです。

AtCoder ABC 138 D - Ki (Go)

irisruneです。休暇を過ごした人もそうでない人もまた心機一転していきましょう。

問題

atcoder.jp

木構造に慣れていれば単純化しやすい問題ですが、計算量には気を付ける必要があります。

package main

import (
    "bufio"
    "fmt"
    "os"
    "strconv"
)

var sc *bufio.Scanner

func nextInt() int {
    sc.Scan()
    i, e := strconv.Atoi(sc.Text())
    if e != nil {
        panic(e)
    }
    return i
}

type node struct {
    weight int
    adj    []int
    parent int
}

func main() {
    sc = bufio.NewScanner(os.Stdin)
    sc.Split(bufio.ScanWords)
    n, q := nextInt(), nextInt()

    tree := make([]node, n+1)
    for i := 0; i < n-1; i++ {
        a, b := nextInt(), nextInt()
        tree[a].adj = append(tree[a].adj, b)
        tree[b].adj = append(tree[b].adj, a)
    }

    for j := 0; j < q; j++ {
        p, x := nextInt(), nextInt()
        tree[p].weight += x
    }

    ans := make([]int, n+1)
    queue := make([]int, 0)
    queue = append(queue, 1)
    for len(queue) > 0 {
        u := queue[0]
        queue = queue[1:]
        ans[u] = ans[tree[u].parent] + tree[u].weight
        for _, v := range tree[u].adj {
            if v == tree[u].parent {
                continue
            }
            tree[v].parent = u
            queue = append(queue, v)
        }
    }

    for i := 1; i < n; i++ {
        fmt.Print(ans[i])
        fmt.Print(" ")
    }
    fmt.Println(ans[n])
}

木をどのように構築するかは様々な方法があると思いますが、今回は頂点を構造体で定義して隣接リストをもつ形を取りました。

まず愚直に解くことを考えてみると、各操作を行うたびに各頂点のカウンターに値を足すことになります。しかし、操作回数がQ回、各操作の対象となる頂点数がO(N)であることから、計算量がO(NQ)となってしまいます。

簡単に思い付く計算量削減の方法としては、各操作を基準となる頂点ごとにまとめてしまうことです。これにより操作にかかる計算量がO(Q)となり全体の計算量がO(N+Q)で抑えられるように見えます。

ここで重要なのが各操作をまとめたものを木に対してどのように反映させるかです。これまた愚直に実装すると各頂点1,2,\ldots,Nにまとめた操作の分量だけ、頂点を根とする部分木の各頂点に対してカウンターの値を増やすという形になりますが、これでは計算量がO(N^2+Q)となってしまいます。

タネを明かせば、各頂点についてその頂点を基準とする操作の分量とその親のカウンターの値を足すことでカウンターの値を求めることができます(頂点1については当然親のカウンターの値は0です)。

この方法でカウンターの値を求めると、計算量をO(N+Q)で抑えることができます。

雑記

  • コンテスト不参加で問題だけ解くのはレートに縛られないのもあってやはり気楽でした。
    • 単純に参加した時に限って難しいD・E問題が続いていたという所もあります。
  • 次回からはまた参加できるよう準備しておきたい所ですね。