技巧
二分 前缀和
思路
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) }