技巧
二分 前缀和
思路
Yi表达式意思 : 统计区间内满足重量大于假设值的数量 * 满足条件的矿石总和
由于Yi 特性(范围操作)。 所以这里用了一手前缀和。统计在X假设下满足条件的数量和总和。然后所有连续区间的操作复杂度都是O(1)的了
最后套用二分模板就行 需要注意的是Y和X是反比关系
实现
// 检验值 = 用区域内比这个物品重的物品数量 * 这些物品的价值之和
// 求检验值与标准值之间最小的差值。
package main
import (
"bufio"
. "fmt"
"io"
"os"
"math"
)
// 尝试x 返回Y
func tryX(stoneArr []Stone, M []Range, x int) int{
// 构建前缀和
sum := make([]int, len(stoneArr))
cnt := make([]int, len(stoneArr))
for i := 1; i < len(stoneArr); i++ {
if stoneArr[i].w >= x {
sum[i] = sum[i - 1] + stoneArr[i].v
cnt[i] = cnt[i - 1] + 1
}else {
sum[i] = sum[i-1]
cnt[i] = cnt[i-1]
}
}
var ret int
// 区间统计
for i := 1; i < len(M); i++ {
ret += (cnt[M[i].r] - cnt[M[i].l - 1]) * (sum[M[i].r] - sum[M[i].l - 1]);
}
return ret
}
// https://ac.nowcoder.com/acm/problem/16597
func NC16597(_r io.Reader, _w io.Writer) {
in, out := bufio.NewReader(_r), bufio.NewWriter(_w)
defer out.Flush()
var n, m, S int
Fscan(in, &n, &m, &S)
stoneArr := make([]Stone, n + 1)
M := make([]Range, m + 1)
for i := 1; i <= n; i++ {
Fscan(in, &stoneArr[i].w, &stoneArr[i].v)
}
for i := 1; i <= m; i++ {
Fscan(in, &M[i].l, &M[i].r)
}
l, r := 0, int(^uint(0) >> 1)
for l <= r {
x := l + (r - l) / 2
if tryX(stoneArr, M, x) <= S {
r = x - 1
}else {
l = x + 1
}
}
Fprintln(out, int(math.Min(math.Abs(float64(S) - float64(tryX(stoneArr, M, r))), math.Abs(float64(S) - float64(tryX(stoneArr, M, l))))))
}
type Stone struct {
w, v int
}
type Range struct {
l, r int
}
func main() {
NC16597(os.Stdin, os.Stdout)
}

京公网安备 11010502036488号