LoginSignup
0
0

More than 1 year has passed since last update.

GoでABC205 Dを2つのやり方で解く

Last updated at Posted at 2021-06-19

概要

ABC205 D問題をGoで二つのやり方で解きます。一つは公式解法で、もう一つは「答えで二分探索」による解法です。ちなみに、サイトに掲載されている解法には、クエリをソートして二分探索なしで解くやり方がありますが、TLE(Time Limit Exceeded) エラーが解消できなかったため、断念しました。

\def\middlemid{\;\middle|\;}
\def\set#1{\left\{{#1}\right\}}
\def\setin#1#2{\left\{{#1} \middlemid {#2}\right\}}
\def\sub#1#2{{#1}_{#2}}

公式の想定解法

ざっくりした方針

$\sub{A}{i}$ごとにその$\sub{A}{i}$より小さく、かつ$A$に含まれない正の整数の個数を$\sub{C}{i}$に記録します。例えば、$A=\set{1,5,6,7}$とすると、$\sub{C}{2}$は、$\sub{A}{2}=6$より小さく、かつ$1,5$を含まないため、$3$となります。
少し飛躍がありますが、答えの$A$に含まれない正の整数を小さい方から数えて$K$番目の数は, その$K$に近い$\sub{C}{i}$を探して、うまく補正すると求めることができます。

コード

package main

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

var reader = bufio.NewReader(os.Stdin)
var writer = bufio.NewWriter(os.Stdout)

func abs(x int) int {
  if x > 0 {
    return x
  } else {
    return x * -1
  }
}

func isOK(a *[]int, index, key int) bool {
    return (*a)[index] >= key
}

func binarySearch(a *[]int, key int) int {
    ng := -1
    ok := len(*a)

    for abs(ok-ng) > 1 {
        mid := (ok + ng) / 2

        if isOK(a, mid, key) {
            ok = mid
        } else {
            ng = mid
        }
    }
    return ok
}

func main() {
    defer writer.Flush()

    var n, q int
    fmt.Fscan(reader, &n, &q)

    var a = make([]int, n)
    for i := 0; i < n; i++ {
        fmt.Fscan(reader, &a[i])
    }

    var c = make([]int, n)
    for i := 0; i < n; i++ {
        if i == 0 {
            c[i] = a[i] - 1
            continue
        }
        c[i] = c[i-1] + a[i] - a[i-1] - 1
    }

    var k = make([]int, q)
    for i := 0; i < q; i++ {
        fmt.Fscan(reader, &k[i])
    }

    for i := 0; i < q; i++ {
        ans := 0
        if c[n-1] < k[i] {
            ans = k[i] - c[n-1] + a[n-1]
        } else {
            idx := binarySearch(&c, k[i])
            ans = a[idx] - 1 - (c[idx] - k[i])
        }
        fmt.Fprintf(writer, "%d\n", ans)
    }
}

「答えで二分探索」による解法

ざっくりした方針

ある正の整数 $m$ から $A$に含まれる$m$以下の整数の個数を引いた数は、$m$以下の$A$に含まれない正の整数の個数になります。これが「答え」なので、あとは二分探索で「答え」が$k$以上を満たす最小の$m$を求めることができます。

コード

package main

import (
    "bufio"
    "fmt"
    "math"
    "os"
)

var reader = bufio.NewReader(os.Stdin)
var writer = bufio.NewWriter(os.Stdout)

const inf = math.MaxInt64

func abs(x int) int {
    if x < 0 {
        return x * -1
    } else {
        return x
    }
}

func isOK(a *[]int, index, key int) bool {
    return (*a)[index] > key
}

func binarySearch(a *[]int, key int) int {
    ng := -1
    ok := len(*a)

    for abs(ok-ng) > 1 {
        mid := (ok + ng) / 2

        if isOK(a, mid, key) {
            ok = mid
        } else {
            ng = mid
        }
    }
    return ok
}

func main() {
    defer writer.Flush()

    var n, q int
    fmt.Fscan(reader, &n, &q)

    var a = make([]int, n)
    for i := 0; i < n; i++ {
        fmt.Fscan(reader, &a[i])
    }

    for i := 0; i < q; i++ {
        var k int
        fmt.Fscan(reader, &k)
        ng := 0
        ok := inf

        for abs(ok-ng) > 1 {
            mid := (ok + ng) / 2
            idx := binarySearch(&a, mid)

            if mid-idx >= k {
                ok = mid
            } else {
                ng = mid
            }
        }

        fmt.Fprintf(writer, "%d\n", ok)
    }

}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0