[R54E]二元组和四元组


数据规模:1≤n,m≤2×1051 \le n,m \le 2 \times 10^5,1≤v≤10181 \le v \le 10^{18},−109≤a1<a2<⋯<an≤109-10^9 \le a_1 < a_2 < \dots < a_n \le 10^9,−109≤b1<b2<⋯<bm≤109-10^9 \le b_1 < b_2 < \dots < b_m \le 10^9。

思路

固定整数 xx,设恰好有 pp 个 aia_i 满足 ai≤xa_i \le x。那么 ii 有 pp 种选择,jj 有 n−pn-p 种选择,所以满足 ai≤x<aja_i \le x < a_j 的二元组 (i,j)(i,j) 共有:

p(n−p)p(n-p)

当 ap≤x<ap+1a_p \le x < a_{p+1} 时,pp 不变,这一段中共有 ap+1−apa_{p+1}-a_p 个整数 xx。同理,若 bq≤y<bq+1b_q \le y < b_{q+1},满足 bk≤y<blb_k \le y < b_l 的二元组 (k,l)(k,l) 共有 q(m−q)q(m-q) 个。因此该 (x,y)(x,y) 的权值是:

p(n−p)q(m−q)p(n-p)q(m-q)

枚举 p=1,2,…,n−1p=1,2,\dots,n-1。记 c=p(n−p)c=p(n-p),则需要找到所有满足:

q(m−q)≥⌈vc⌉q(m-q)\ge \left\lceil\frac{v}{c}\right\rceil

的 qq。函数 q(m−q)q(m-q) 在 1≤q≤⌊m/2⌋1\le q\le \lfloor m/2\rfloor 上单调递增,并且关于 m/2m/2 对称。若左半段中最小的合法位置为 LL,那么所有合法位置恰好构成连续区间:

[L,m−L][L,m-L]

可以二分求出 LL。这些位置对应的整数 yy 的数量为相邻差之和,即:

∑q=Lm−L(bq+1−bq)=bm−L+1−bL\sum_{q=L}^{m-L}(b_{q+1}-b_q)=b_{m-L+1}-b_L

将它乘上当前区间内整数 xx 的数量 ap+1−apa_{p+1}-a_p,再对所有 pp 求和即可。实现中用 (v−1)/c+1(v-1)/c+1 计算上取整,避免乘积 p(n−p)q(m−q)p(n-p)q(m-q) 超出 Int64。

复杂度

时间复杂度为 O(nlog⁡m)O(n\log m),空间复杂度为 O(n+m)O(n+m)。

仓颉实现

查看仓颉实现
import std.convert.*
import std.env.*

func countValidY(b: Array<Int64>, m: Int64, need: Int64): Int64 {
    let half = m / 2
    if (half * (m - half) < need) {
        return 0
    }

    var left = Int64(1)
    var right = half
    while (left < right) {
        let mid = (left + right) >> 1
        if (mid * (m - mid) >= need) {
            right = mid
        } else {
            left = mid + 1
        }
    }
    return b[m - left] - b[left - 1]
}

main() {
    let reader = getStdIn()
    let first = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
    let n = first[0]
    let m = first[1]
    let v = first[2]
    let a = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
    let b = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })

    var answer = Int64(0)
    var p = Int64(1)
    while (p < n) {
        let pairCount = p * (n - p)
        let need = (v - 1) / pairCount + 1
        let xCount = a[p] - a[p - 1]
        answer += xCount * countValidY(b, m, need)
        p += 1
    }

    println(answer)
}