結果

問題 No.952 危険な火薬庫
ユーザー 草苺奶昔草苺奶昔
提出日時 2023-12-03 02:04:41
言語 Go
(1.22.1)
結果
RE  
実行時間 -
コード長 5,708 bytes
コンパイル時間 12,036 ms
コンパイル使用メモリ 220,836 KB
実行使用メモリ 6,528 KB
最終ジャッジ日時 2024-09-26 21:56:15
合計ジャッジ時間 26,503 ms
ジャッジサーバーID
(参考情報)
judge4 / judge5
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 1 ms
5,248 KB
testcase_01 RE -
testcase_02 RE -
testcase_03 RE -
testcase_04 RE -
testcase_05 RE -
testcase_06 RE -
testcase_07 RE -
testcase_08 RE -
testcase_09 RE -
testcase_10 RE -
testcase_11 RE -
testcase_12 RE -
testcase_13 RE -
testcase_14 RE -
testcase_15 RE -
testcase_16 RE -
testcase_17 RE -
testcase_18 RE -
testcase_19 RE -
testcase_20 RE -
testcase_21 RE -
testcase_22 RE -
testcase_23 RE -
testcase_24 RE -
testcase_25 RE -
権限があれば一括ダウンロードができます

ソースコード

diff #

package main

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

func main() {
	Yuki952()
	// Yuki705()
}

// 危险的火药库
// 有n个火药库,每个火药库有一个危险程度A[i].
// 现在需要打开某些火药库.
// 总危险程度为 `被打开的火药库连续段的危险程度之和`的平方和.
// 对k=1,...,n,求打开k个火药库时的最小总危险程度.
// n<=3000, A[i]<=1e6
// https://yukicoder.me/problems/no/952
func Yuki952() {
	in := bufio.NewReader(os.Stdin)
	out := bufio.NewWriter(os.Stdout)
	defer out.Flush()

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

	preSum := make([]int, n+1)
	for i := 0; i < n; i++ {
		preSum[i+1] = preSum[i] + nums[i]
	}

	// 0 -> n+1 的路径
	// i -> j 的边权为: (sum_{k=i...j-2} nums[k])^2
	cost := func(i, j int) int {
		if j-i <= 1 {
			return 0
		}
		tmp := preSum[j-1] - preSum[i]
		return tmp * tmp
	}

	res := EnumerateMongeDEdgeShortestPath(n+1, cost)
	for k := 1; k <= n; k++ {
		fmt.Fprintln(out, res[n+1-k])
	}

	for i := 1; i < n+1; i++ {
		res2 := MongeShortestPathDEdge(n+1, i, 1e18, cost)
		if res2 != res[i] {
			panic("res2 != res[i]")
		}
	}
}

// 清理垃圾
// https://yukicoder.me/problems/no/705
func Yuki705() {
	in := bufio.NewReader(os.Stdin)
	out := bufio.NewWriter(os.Stdout)
	defer out.Flush()

	var n int
	fmt.Fscan(in, &n)
	A := make([]int, n)
	X := make([]int, n)
	Y := make([]int, n)
	for i := 0; i < n; i++ {
		fmt.Fscan(in, &A[i])
	}
	for i := 0; i < n; i++ {
		fmt.Fscan(in, &X[i])
	}
	for i := 0; i < n; i++ {
		fmt.Fscan(in, &Y[i])
	}

	cubic := func(x int) int {
		return abs(x * x * x)
	}
	// 人i,垃圾j
	dist := func(i, j int) int {
		return cubic(X[j]-A[i]) + cubic(Y[j])
	}
	f := func(i, j int) int {
		return dist(j-1, i)
	}
	fmt.Fprintln(out, MongeShortestPath(n, f)[n])
}

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

const INF int = 1e18

// Monge图最短路.求从0点出发到各个点的最短路.
// O(nlogn).
// f(i,j) : 边权函数, 即从i到j的边的权值(0<=i<j<=n).
// https://noshi91.hatenablog.com/entry/2023/02/18/005856
func MongeShortestPath(n int, f func(i, j int) int) []int {
	dp := make([]int, n+1)
	for i := range dp {
		dp[i] = INF
	}
	x := make([]int, n+1)

	check := func(from, to int) {
		if from >= to {
			return
		}
		cost := f(from, to)
		if tmp := dp[from] + cost; tmp < dp[to] {
			dp[to] = tmp
			x[to] = from
		}
	}

	var dfs func(int, int)
	dfs = func(l, r int) {
		if l+1 >= r {
			return
		}
		m := (l + r) / 2
		for i := x[l]; i <= x[r]; i++ {
			check(i, m)
		}
		dfs(l, m)
		for i := l + 1; i <= m; i++ {
			check(i, r)
		}
		dfs(m, r)
	}
	dp[0] = 0
	check(0, n)
	dfs(0, n)
	return dp
}

// Monge图d边最短路.
// O(nlogn)求从0到n恰好经过d条边的最短路,其中边权f(from,to)满足Monge性质.
// f(i,j) : 边权函数, 即从i到j的边的权值(0<=i<j<=n).
// maxWeight: 边权的最大值.
// 返回值为从0到N恰好经过d条边的最短路.
func MongeShortestPathDEdge(n, d, maxWeight int, f func(i, j int) int) int {
	if d > n {
		panic("d > N")
	}
	cal := func(x int) int {
		g := func(frm, to int) int {
			return f(frm, to) + x
		}
		cost := MongeShortestPath(n, g)[n]
		return cost - x*d
	}
	_, res := FibonacciSearch(cal, -3*maxWeight, 3*maxWeight+1, false)
	return res
}

// monge图从0到n,经过d边最短路的d=1,...,N的答案.
// 无法到达的点的距离为INF.
// f(i,j) : 边权函数, 即从i到j的边的权值(0<=i<j<=n).
// O(n^2logn)
func EnumerateMongeDEdgeShortestPath(n int, f func(i, j int) int) []int {
	res := make([]int, n+1)
	for i := range res {
		res[i] = INF
	}
	dp := make([]int, n+1)
	for i := range dp {
		dp[i] = INF
	}
	dp[0] = 0
	for d := 1; d <= n; d++ {
		midx := MonotoneMinima2(n+1, n+1, func(j, i int) int {
			if i < j {
				return dp[i] + f(i, j)
			}
			return INF
		})
		for i := n; i >= d; i-- {
			dp[i] = dp[midx[i]] + f(midx[i], i)
		}
		res[d] = dp[n]
	}
	return res
}

// 给定一个 n 行 m 列的矩阵.
// minI=argmin_j(A[i][j]) 单调递增时, 返回 minI.
// f(i, j, k) :
// 比较 A[i][j] 和 A[i][k] (保证 j < k)
// 当 A[i][j] <= A[i][k] 时返回 true.
func MonotoneMinima(row, col int, f func(i, j, k int) bool) []int {
	res := make([]int, row)
	var dfs func(int, int, int, int)
	dfs = func(is, ie, l, r int) {
		if is == ie {
			return
		}
		i := (is + ie) / 2
		m := l
		for k := l + 1; k < r; k++ {
			if !f(i, m, k) {
				m = k
			}
		}
		res[i] = m
		dfs(is, i, l, m+1)
		dfs(i+1, ie, m, r)
	}
	dfs(0, row, 0, col)
	return res
}

// 给定一个 n 行 m 列的矩阵.
// minI=argmin_j(A[i][j]) 单调递增时, 返回 minI.
// get(i, j) : 返回 A[i][j] 的函数
func MonotoneMinima2(row, col int, get func(i, j int) int) []int {
	f := func(i, j, k int) bool {
		return get(i, j) <= get(i, k)
	}
	return MonotoneMinima(row, col, f)
}

// 寻找[start,end)中的一个极值点,不要求单峰性质.
//
//	返回值: (极值点,极值)
func FibonacciSearch(f func(x int) int, start, end int, minimize bool) (int, int) {
	end--
	a, b, c, d := start, start+1, start+2, start+3
	n := 0
	for d < end {
		b = c
		c = d
		d = b + c - a
		n++
	}

	get := func(i int) int {
		if end < i {
			return INF
		}
		if minimize {
			return f(i)
		}
		return -f(i)
	}

	ya, yb, yc, yd := get(a), get(b), get(c), get(d)
	for i := 0; i < n; i++ {
		if yb < yc {
			d = c
			c = b
			b = a + d - c
			yd = yc
			yc = yb
			yb = get(b)
		} else {
			a = b
			b = c
			c = a + d - b
			ya = yb
			yb = yc
			yc = get(c)
		}
	}

	x := a
	y := ya
	if yb < y {
		x = b
		y = yb
	}
	if yc < y {
		x = c
		y = yc
	}
	if yd < y {
		x = d
		y = yd
	}

	if minimize {
		return x, y
	}
	return x, -y

}
0