牛客周赛 Round 70 F 题解

题目链接

题目大意

Definition1

我们认为非空数组 a a a的,当且仅当 a a a 满足下列其中一个条件:

  • ∣ a ∣ = 1 |a| = 1 a=1
  • ∀ i ∈ [ 1 ,   ∣ a ∣ ] \forall i \in [1, \ |a|] i[1, a],一定存在 j ∈ [ 1 ,   i ) ∪ ( i ,   ∣ a ∣ ] j \in [1, \ i) \cup (i, \ |a|] j[1, i)(i, a],有 a i ∣ a j a_i \mid a_j aiaj a j ∣ a i a_j \mid a_i ajai

其中 ∣ a ∣ |a| a 表示数组 a a a 的长度, x ∣ y x \mid y xy 表示 x x x 整除 y y y,例如 2 2 2 能整除 4 4 4

Definition2

我们定义数组 a a a极好的,当且仅当 a a a 的任意非空连续子数组都是的。

例如连续子数组 a [ l . . . r ] a[l...r] a[l...r] 表示 a l ,   a l + 1 ,   ⋯   ,   a r a_l, \ a_{l + 1}, \ \cdots \ , \ a_r al, al+1,  , ar,其中 1 ⩽ l ⩽ r ⩽ ∣ a ∣ 1 \leqslant l \leqslant r \leqslant |a| 1lra

Definition3

我们定义数组 a a a 加上整数 x x x 的结果为 a ′ a' a,即令 a ′ = a + x a' = a + x a=a+x a ′ a' a 需要满足:

  • ∣ a ′ ∣ = ∣ a ∣ |a'| = |a| a=a
  • ∀ i ∈ [ 1 ,   ∣ a ∣ ] \forall i \in [1, \ |a|] i[1, a],均有 a i ′ = a i + x a'_i = a_i + x ai=ai+x

Question

T T T 组数据,每组数据给定一个数组 a a a 和一个正整数 k k k,求有多少整数 x ∈ [ 1 ,   k ] x \in [1, \ k] x[1, k] 满足 a + x a + x a+x极好的数组。

数据范围

  • 1 ⩽ ∣ a ∣ ⋅ T ⩽ 6 × 1 0 4 1 \leqslant |a| \cdot T \leqslant 6 \times 10^4 1aT6×104
  • 1 ⩽ a i ,   k ⩽ 1 0 9 1 \leqslant a_i, \ k \leqslant 10^9 1ai, k109

Solution

根据定义,我们可以得到数组 a a a极好的 的充要条件:对 ∀ i ∈ [ 1 , ∣ a ∣ ) \forall i \in [1, |a|) i[1,a),均有 a i ∣ a i + 1 a_i \mid a_{i + 1} aiai+1 a i + 1 ∣ a i a_{i + 1} \mid a_i ai+1ai

下面来证明一下。

充分性( ⇐ \Leftarrow ):

首先,数组 a a a 只要满足 ∣ a ∣ = 1 |a| = 1 a=1 就是好的,也是极好的。
再设 a [ l . . . r ] a[l...r] a[l...r] 这个子数组是好的,由上述条件,可以推出 a [ l . . . ( r + 1 ) ] a[l...(r + 1)] a[l...(r+1)]是好的,因为 a r ∣ a r + 1 a_r \mid a_{r + 1} arar+1 a r + 1 ∣ a r a_{r + 1} \mid a_r ar+1ar
由数学归纳法, a a a 的任意连续子数组是好的,那么 a a a 就是极好的。

必要性( ⇒ \Rightarrow ):

若数组 a a a 是极好的,可以得到 a a a 的任意连续子数组是好的,那么长度为 2 2 2 的连续子数组也是好的,换句话说就是,对 ∀ i ∈ [ 1 ,   ∣ a ∣ ) \forall i \in [1, \ |a|) i[1, a),都有 a i ∣ a i + 1 a_i \mid a_{i + 1} aiai+1 a i + 1 ∣ a i a_{i + 1} \mid a_i ai+1ai(根据好数组的定义)。

有了这个充要条件,我们就可以 O ( ∣ a ∣ ) O(|a|) O(a) 判断数组 a a a 是否是极好的。

但现在要判断的是 a + x a + x a+x 是否极好,暴力枚举是 O ( 1 0 9 ∣ a ∣ ) O(10^9|a|) O(109a),因此还需要进一步挖掘性质。

首先,如果 a a a 的所有元素都相同,那么 a a a 一定是极好的,且 a + x a + x a+x 也一定是极好的,这种情况下 x x x k k k 种选法。

下面考虑 a a a 存在不同元素的情况。

根据充要条件,我们需要对相邻两项的整除关系做限制,所以我们可以先考虑用其中一个 a i a_i ai a i + 1 a_{i + 1} ai+1 来限制 x x x 的选取。下面进行详细说明。

a ′ = a + x a' = a + x a=a+x,若 a ′ a' a 是极好的,说明任意一个长度为 2 2 2 的连续子数组都是好的,且由于 a a a 存在不同元素,所以我们一定可以在某个 i ∈ [ 1 ,   ∣ a ∣ ) i \in [1, \ |a|) i[1, a) 取这样两个数:

  • u = min ⁡ ( a i ,   a i + 1 ) ,   v = max ⁡ ( a i ,   a i + 1 ) u = \min(a_i, \ a_{i + 1}), \ v = \max(a_i, \ a_{i + 1}) u=min(ai, ai+1), v=max(ai, ai+1),满足 u < v u < v u<v

a ′ a' a 是极好的可以得到 ( u + x ) ∣ ( v + x ) (u + x) \mid (v + x) (u+x)(v+x),那么就有 ( v + x ) = t ( u + x ) ( t > 1 ) (v + x) = t(u + x) (t > 1) (v+x)=t(u+x)(t>1),那么就有 v − u = ( t − 1 ) ( u + x ) v - u = (t - 1)(u + x) vu=(t1)(u+x),令 d = v − u d = v - u d=vu,会得到 ( u + x ) ∣ d (u + x) \mid d (u+x)d,即 u u u 加上一个 x x x 后是 d d d 的约数。

反过来说,我们可以找到这样的 u ,   v u, \ v u, v,然后枚举 d = v − u d = v - u d=vu 的约数 d i v div div(即上一段的 ( u + x ) (u + x) (u+x)),看其是否满足 d i v > u div > u div>u d i v ⩽ u + k div \leqslant u + k divu+k,如果是,那说明 x = d i v − u x = div - u x=divu,再用上面的充要条件 O ( n ) O(n) O(n) 判断 a + x a + x a+x 是否是极好的。

时间复杂度 O ( n V ) O(n\sqrt{V}) O(nV )
  • 其中 V V V 为值域范围,题中 V = 1 0 9 V = 10^9 V=109
  • 但其实复杂度远远达不到 V \sqrt{V} V ,因为对于 1 0 9 10^9 109 内的数,其约数个数最大为 1344 1344 1344
  • 总复杂度跑满的话计算量大约是 1 0 8 10^8 108

C++ Code

#include <bits/stdc++.h>

using i64 = int64_t;

void solve() {
    int n, k;
    std::cin >> n >> k;

    std::vector<int> a(n);
    for (int i = 0; i < n; i++) {
        std::cin >> a[i];
    }

    int u = -1;
    int v = -1;
    for (int i = 0; i + 1 < n; i++) {
        if (a[i] != a[i + 1]) {
            u = std::min(a[i], a[i + 1]);
            v = std::max(a[i], a[i + 1]);
            break;
        }
    }
    int d = v - u;

    if (d == 0) {
        std::cout << k << " " << k * (k + 1LL) / 2 << "\n";
        return;
    }

    int cnt = 0;
    i64 sum = 0;
    auto work = [&](int x) {
        x -= u;
        if (x <= 0 or x > k) {
            return;
        }
        auto b = a;
        for (int &y: b) {
            y += x;
        }
        for (int i = 1; i < n; i++) {
            if (std::max(b[i], b[i - 1]) % std::min(b[i], b[i - 1])) {
                return;
            }
        }
        cnt++;
        sum += x;
    };
    for (int i = 1; i * i <= d; i++) {
        if (d % i == 0) {
            work(i);
            if (i * i != d) {
                work(d / i);
            }
        }
    }
    std::cout << cnt << " " << sum << "\n";
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int T = 1;
    std::cin >> T;
    
    while (T--) {
        solve();
    }
    
    return 0;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值