【牛客小白月赛 113】题解

比赛链接

A. 2025

Solution

模拟即可。

C++ Code

#include <bits/stdc++.h>

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    std::string s;
    std::cin >> s;

    int sum = 1;
    for (auto c: s) {
        if (c == '-') {
            sum--;
        } else {
            sum *= 2;
        }
        if (sum >= 2025) {
            std::cout << "YES\n";
            return 0;
        }
    }
    std::cout << "NO\n";
    
    return 0;
}

B. 好字符串

Solution

直接统计有多少 i ∈ ( 1 , n ] i \in (1, n] i(1,n] 满足 s i − 1 = s i s_{i - 1} = s_i si1=si,记为 c n t \rm{cnt} cnt,只有 c n t ≤ 1 \rm{cnt \leq 1} cnt1 才满足条件。

C++ Code

#include <bits/stdc++.h>

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n;
    std::cin >> n;

    std::string s;
    std::cin >> s;

    int cnt = 0;
    for (int i = 1; i < n; i++) {
        cnt += s[i - 1] == s[i];
    }
    std::cout << (cnt <= 1 ? "YES\n": "NO\n");
    
    return 0;
}

C. 连续数组

Solution

只要看每个 a i a_i ai 是否升序,且整体无重复元素,并且 max ⁡ − min ⁡ + 1 \max - \min + 1 maxmin+1 要与集合大小相等。

C++ Code

#include <bits/stdc++.h>

template<class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {
    for (auto &x: v) {
        is >> x;
    }
    return is;
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int q;
    std::cin >> q;

    std::set<int> set;
    for (int i = 0; i < q; i++) {
        int k;
        std::cin >> k;
        std::vector<int> a(k);
        std::cin >> a;
        if (not std::ranges::is_sorted(a)) {
            std::cout << "NO\n";
            return 0;
        }
        for (int x: a) {
            if (set.contains(x)) {
                std::cout << "NO\n";
                return 0;
            }
            set.insert(x);
        }
    }

    if (*set.rbegin() - *set.begin() + 1 != set.size()) {
        std::cout << "NO\n";
    } else {
        std::cout << "YES\n";
    }
    
    return 0;
}

D. mex

题目大意

给定一个长度为 n n n 的非负整数数组 a a a,要求做一下操作若干次,使得 a a a 里面的数都相同。

  • m e x = m e x ( a ) \rm{mex = mex(a)} mex=mex(a),然后对 ∀ i ∈ [ 1 , n ] \forall i \in [1, n] i[1,n],令 a i : = max ⁡ ( 0 , a i − m e x ) \rm{a_i := \max(0, a_i - mex)} ai:=max(0,aimex)

求至少要操作几次(可以不操作),如果无法做到,输出 − 1 -1 1

其中 m e x ( a ) \rm{mex(a)} mex(a) 表示 a a a 中未出现的最小非负整数,例如 m e x ( { 0 , 1 , 3 } ) = 2 \rm{mex(\{0, 1, 3 \})} = 2 mex({0,1,3})=2

数据范围

  • 1 ≤ n ≤ 1 0 5 , 1\leq n \leq 10^5, 1n105,
  • 0 ≤ a i ≤ 1 0 18 . 0 \leq a_i \leq 10^{18}. 0ai1018.

Solution

首先对 a a a 排序,若 a 1 ≠ 0 a_1 \neq 0 a1=0,说明 m e x ( a ) \rm{mex(a)} mex(a) 只能是 0 0 0,也就是无法修改 a a a,因此只要看 a a a 的数是否全相等即可。

否则我们记录当前想要出现的 m e x \rm{mex} mex、整个数组被减去的量 s u b \rm{sub} sub 以及操作次数 a n s \rm{ans} ans,初值均为 0 0 0

若对于当前 x x x,令 x : = x − s u b x := x - sub x:=xsub

  • x = m e x \rm{x = mex} x=mex,令 m e x : = m e x + 1 \rm{mex := mex + 1} mex:=mex+1
  • x < m e x \rm{x < mex} x<mex,直接 continue
  • 否则这个 m e x \rm{mex} mex 就真的出现了,直接令 x : = x − m e x , s u b : = s u b + m e x , a n s : = a n s + , 1. x := x - mex, \\ sub := sub + mex, \\ ans := ans + ,1. x:=xmex,sub:=sub+mex,ans:=ans+1. 如果此时仍有 x > 1 x > 1 x>1,那说明 m e x = 1 \rm{mex} = 1 mex=1,只要减 x − 1 x - 1 x1 次就能令 x = 1 x = 1 x=1,然后我们再令 m e x = 2 \rm{mex} = 2 mex=2,继续往后遍历。

最后如果剩下的 m e x > 1 \rm{mex} > 1 mex>1,说明一定有没处理完的,答案再 + 1 +1 +1

时间复杂度 O ( n ) O(n) O(n)

C++ Code

#include <bits/stdc++.h>

using i64 = long long;

template<class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {
    for (auto &x: v) {
        is >> x;
    }
    return is;
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n;
    std::cin >> n;

    std::vector<i64> a(n);
    std::cin >> a;

    std::ranges::sort(a);

    if (a[0] != 0) {
        std::cout << (a[0] != a[n - 1] ? -1: 0) << "\n";
        return 0;
    }

    i64 sub = 0;
    i64 mex = 0;
    i64 ans = 0;
    for (i64 &x: a) {
        x -= sub;
        if (x == mex) {
            mex++;
            continue;
        }
        if (x < mex) {
            continue;
        }
        x -= mex;
        sub += mex;
        ans++;
        if (x > 1) {
            sub += x - 1;
            ans += x - 1;
        }
        mex = 2;
    }
    ans += mex > 1;

    std::cout << ans << "\n";
    
    return 0;
}

E. 可划分数组

题目大意

对于一个长度为 n n n 的数组 a a a,将其分成 k k k 份,第 i i i 份的下标区间是 [ l i , r i ] [l_i, r_i] [li,ri],要求:

  • l 1 = 1 , l_1 = 1, l1=1,
  • r k = n , r_k = n, rk=n,
  • ∀ i ∈ [ 1 , k ] ,   l i < r i , \forall i \in [1, k], \ l_i < r_i, i[1,k], li<ri,
  • ∀ i ∈ ( 1 , k ] ,   l i = r i − 1 + 1 , \forall i \in (1, k], \ l_i = r_{i - 1} + 1, i(1,k], li=ri1+1,
  • ∀ p ∈ [ l i , r i ] ,  至少存在一个  q ∈ [ l i , r i ] ∖ p ,使得 gcd ⁡ ( a p , a q ) ≠ 1. \forall p \in [l_i, r_i], \ 至少存在一个 \ q \in [l_i, r_i] \setminus {p},使得 \gcd(a_p, a_q) \neq 1. p[li,ri], 至少存在一个 q[li,ri]p,使得gcd(ap,aq)=1.

k k k 的可能最大值,如果无法进行分割,输出 − 1 -1 1

数据范围

  • 1 ≤ n ≤ 2000 , 1 \leq n \leq 2000, 1n2000,
  • 1 ≤ a i ≤ 1 0 9 . 1 \leq a_i \leq 10^9. 1ai109.

Solution

赛时我用了非常麻烦的分解质因数,然后对位置做标记什么的才过的,下面介绍简单方法。

首先,若 n = 1 n = 1 n=1 或者 ∃ a i = 1 \exists a_i = 1 ai=1,那么一定无法分割。

否则我们考虑 d p \rm{dp} dp,设 d p [ i ] \rm{dp[i]} dp[i] 表示考虑对 [ 1 , i ] [1, i] [1,i] 做分割,能得到的最大 k k k

显然有转移方程

d p [ i ] = max ⁡ j ∈ [ 1 , i ) d p [ j − 1 ] + 1 dp[i] = \max\limits_{j \in [1, i)}dp[j - 1] + 1 dp[i]=j[1,i)maxdp[j1]+1

其中 j j j 需要满足区间 [ j , i ] [j, i] [j,i] 是合法的。那么问题变成了如何在 O ( 1 ) O(1) O(1) O ( log ⁡ n ) O(\log n) O(logn) 的时间判定一段区间 [ l , r ] [l, r] [l,r] 是否合法。

我们令 p r e [ i ] \rm{pre[i]} pre[i] 表示 [ 1 , i ) [1, i) [1,i) 内最靠右的 j j j,使得 gcd ⁡ ( a i , a j ) ≠ 1 \gcd(a_i, a_j) \neq 1 gcd(ai,aj)=1,如果没有设为 0 0 0
接着令 n x t [ i ] \rm{nxt[i]} nxt[i] 表示 ( i , n ] (i, n] (i,n] 内最靠左的 j j j,使得 gcd ⁡ ( a i , a j ) ≠ 1 \gcd(a_i, a_j) \neq 1 gcd(ai,aj)=1,如果没有设为 n + 1 n + 1 n+1

这两个数组可以提前 O ( n 2 log ⁡ max ⁡ ( a ) ) O(n^2\log\max(a)) O(n2logmax(a)) 预处理。

对一段区间 [ l , r ] [l, r] [l,r] 来说,如果它是合法的,当且仅当

  • ∀ i ∈ [ l , r ] \forall i \in [l, r] i[l,r],均有 p r e [ i ] ≥ l ∣ ∣ n x t [ i ] ≤ r pre[i] \geq l \mid \mid nxt[i] \leq r pre[i]l∣∣nxt[i]r

那么对于某个 i i i,当 p r e [ i ] ≥ l pre[i] \geq l pre[i]l 时,无需看 n x t [ i ] nxt[i] nxt[i],否则就要看所有遍历过的 n x t [ i ] nxt[i] nxt[i] 的最大值是否 ≤ r \leq r r

简单来说,就是对于固定的 l l l r r r l l l 开始往右遍历,若 p r e [ r ] < l pre[r] < l pre[r]<l,说明当前 [ l , r ] [l, r] [l,r] 肯定是废了(因为 r r r 就是右端点,不能再往右找 j j j 了),只要令 R = max ⁡ ( R , n x t [ i ] ) R = \max(R, nxt[i]) R=max(R,nxt[i]),否则若 R ≤ r R \leq r Rr,就可以更新 d p [ r ] dp[r] dp[r] 的状态。

时间复杂度 O ( n 2 log ⁡ max ⁡ ( a ) ) O(\rm{n^2\log\max(a)}) O(n2logmax(a))

  • 这个 log ⁡ \log log 是求 gcd ⁡ \gcd gcd 时产生的,其实快得很,甚至我感觉就比 O ( 1 ) O(1) O(1) 慢一丢丢。

C++ Code

#include <bits/stdc++.h>

template<class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {
    for (auto &x: v) {
        is >> x;
    }
    return is;
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n;
    std::cin >> n;

    std::vector<int> a(n);
    std::cin >> a;

    if (n == 1 or std::ranges::count(a, 1) > 0) {
        std::cout << -1 << "\n";
        return 0;
    }

    std::vector pre(n, -1);
    for (int i = 0; i < n; i++) {
        for (int j = i - 1; j >= 0; j--) {
            if (std::gcd(a[i], a[j]) != 1) {
                pre[i] = j;
                break;
            }
        }
    }
    std::vector nxt(n, n);
    for (int i = 0; i < n; i++) {
        for (int j = i + 1; j < n; j++) {
            if (std::gcd(a[i], a[j]) != 1) {
                nxt[i] = j;
                break;
            }
        }
    }

    std::vector<int> dp(n + 1, -1);
    dp[0] = 0;
    for (int l = 0; l < n; l++) {
        for (int r = l, R = 0; r < n and dp[l] != -1; r++) {
            if (pre[r] < l) {
                R = std::max(R, nxt[r]);
            } else if (R <= r) {
                dp[r + 1] = std::max(dp[r + 1], dp[l] + 1);
            }
        }
    }
    std::cout << dp.back() << "\n";

    return 0;
}

F. 最小差值

题目大意

给定整数数组 a , b a, b a,b,长度分别为 n , m n, m n,m,现在请找一个整数 k ∈ [ − 2 ⋅ 1 0 9 ,   2 ⋅ 1 0 9 ] k \in [-2 \cdot 10^9, \ 2 \cdot 10^9] k[2109, 2109],使得 ∣ ( ∑ i = 1 n ∣ a i − k ∣ ) − ( ∑ j = 1 m ∣ b j − k ∣ ) ∣ \left|\left(\sum\limits_{i = 1}^{n}|a_i - k|\right) - \left(\sum\limits_{j = 1}^{m}|b_j - k|\right)\right| (i=1naik)(j=1mbjk) 最小,输出满足条件的最小 k k k

数据范围

  • 1 ≤ n , m ≤ 1 0 6 , 1 \leq n, m \leq 10^6, 1n,m106,
  • − 1 0 9 ≤ a i , b i ≤ 1 0 9 . -10^9 \leq a_i, b_i \leq 10^9. 109ai,bi109.

Solution

这种找一个数使得某种求和最小,我们最简单的想法就是直接令 k = a i k = a_i k=ai k = b j k = b_j k=bj

但是显然我们可以找个很简单的例子 h a c k \rm{hack} hack,比如 a = { − 2 , 1 } ,   b = { 1 , 2 } a = \{-2, 1\}, \ b = \{1, 2\} a={2,1}, b={1,2},在 k = 0 k = 0 k=0 时取到最小值。

不过这个思路可以保留做拓展,因为 k ∈ ( − 2 , 1 ) k \in (-2, 1) k(2,1),只要再我们探究一下 k ≠ a i k \neq a_i k=ai k ≠ b j k \neq b_j k=bj 的规律,说不定就能搞定。

若现在 k ∈ a ∪ b k \in a \cup b kab,但 k + 1 ∉ a ∪ b k + 1 \notin a \cup b k+1/ab,我们令 f ( k ) = ( ∑ i = 1 n ∣ a i − k ∣ ) − ( ∑ j = 1 m ∣ b j − k ∣ ) , f(k) = \left(\sum\limits_{i = 1}^{n}|a_i - k|\right) - \left(\sum\limits_{j = 1}^{m}|b_j - k|\right), f(k)=(i=1naik)(j=1mbjk), 接着计算 f ( k + 1 ) − f ( k ) f(k + 1) - f(k) f(k+1)f(k)

  • 对于 a i ≤ k a_i \leq k aik i i i,数量是没变的,设为 c a 1 c_{a_1} ca1,会对表达式产生 c a 1 c_{a_1} ca1 的增量;
  • 对于 a i > k a_i > k ai>k i i i,数量同样不变,设为 c a 2 c_{a_2} ca2,会对表达式产生 − c a 2 -c_{a_2} ca2 的增量;
  • 对于 b j ≤ k b_j \leq k bjk j j j,数量是没变的,设为 c b 1 c_{b_1} cb1,会对表达式产生 − c b 1 -c_{b_1} cb1 的增量;
  • 对于 b j < k b_j < k bj<k j j j,数量同样不变,设为 c b 2 c_{b_2} cb2,会对表达式产生 c b 2 c_{b_2} cb2 的增量。

这样一来总的增量就是 A = c a 1 + c b 2 − c a 2 − c b 1 A = c_{a_1} + c_{b_2} - c_{a_2} - c_{b_1} A=ca1+cb2ca2cb1

k + L ∈ a ∪ b k + L \in a \cup b k+Lab,于是对于 ∀ x ∈ ( k , k + L ) \forall x \in (k, k + L) x(k,k+L),都有 f ( x ) = f ( k ) + A ( x − k ) f(x) = f(k) + A(x - k) f(x)=f(k)+A(xk) 如果存在一个 x x x 使得 f ( x ) = 0 f(x) = 0 f(x)=0,那 ∣ f ( x ) ∣ = 0 |f(x)| = 0 f(x)=0,一定最优。因此要找到这样的 x x x,就先令 f ( x ) = 0 f(x) = 0 f(x)=0,然后化简得到 x k = k − f ( k ) A . x_k = k - \frac{f(k)}{A}. xk=kAf(k). 由于这个 x k x_k xk 不是整数,所以我们只要找靠近它的两个值 ⌊ x k ⌋ \lfloor x_k \rfloor xk ⌈ x k ⌉ \lceil x_k \rceil xk 即可。

因此最终我们需要找的点就有:

  • ∀ x ∈ a ∪ b , \forall x \in a \cup b, xab,
  • ∀ ⌊ x k ⌋ , ⌈ x k ⌉ . \forall \lfloor x_k \rfloor, \lceil x_k \rceil. xk,xk.(用上述方式计算得到的)

时间复杂度 O ( ( n + m ) log ⁡ ( n + m ) ) O(\rm{(n + m)\log (n + m)}) O((n+m)log(n+m))

C++ Code (my implementation)

#include <bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned;
using i80 = __int128_t;
using u80 = unsigned __int128_t;
using f64 = double;
using f80 = long double;
using i16 = short;
using u16 = unsigned short;

constexpr f64 pi = std::numbers::pi;

template<class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {
    for (auto &x: v) {
        is >> x;
    }
    return is;
}

constexpr int inf = 2E9;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n, m;
    std::cin >> n >> m;

    std::vector<int> a(n);
    std::cin >> a;

    std::vector<int> b(m);
    std::cin >> b;

    std::ranges::sort(a);
    std::ranges::sort(b);

    std::vector<int> v{-inf, inf};
    v.insert(v.end(), a.begin(), a.end());
    v.insert(v.end(), b.begin(), b.end());

    std::ranges::sort(v);

    std::vector<i64> sa(n + 1);
    for (int i = 0; i < n; i++) {
        sa[i + 1] = sa[i] + a[i];
    }
    std::vector<i64> sb(m + 1);
    for (int i = 0; i < m; i++) {
        sb[i + 1] = sb[i] + b[i];
    }
    auto calc = [&](int k) {
        i64 sum = 0;
        int i = std::ranges::upper_bound(a, k) - a.begin();
        sum += static_cast<i64>(i) * k - sa[i];
        sum += (sa[n] - sa[i]) - static_cast<i64>(n - i) * k;
        int j = std::ranges::upper_bound(b, k) - b.begin();
        sum -= static_cast<i64>(j) * k - sb[j];
        sum -= (sb[m] - sb[j]) - static_cast<i64>(m - j) * k;
        return sum;
    };

    std::vector<int> nv;
    for (int i = 0; i + 1 < v.size(); i++) {
        int l = v[i];
        int r = v[i + 1];
        if (l + 1 >= r) {
            continue;
        }
        i64 lv = calc(l);
        i64 rv = calc(r);
        auto sgn = [&](i64 x) {
            return x != 0 ? x / std::abs(x): 0;
        };
        if (sgn(lv) * sgn(rv) > 0) {
            continue;
        }
        int ai = std::ranges::upper_bound(a, l) - a.begin();
        int bj = std::ranges::upper_bound(b, l) - b.begin();
        int A = ai - (n - ai) - (bj - (m - bj));
        if (A == 0) {
            continue;
        }
        f64 kA = l - static_cast<f64>(lv) / A;
        for (auto k: {std::floor(kA), std::ceil(kA)}) {
            if (l < k and k < r) {
                nv.push_back(int(k));
            }
        }
    }

    v.insert(v.end(), nv.begin(), nv.end());
    std::ranges::sort(v);
    v.erase(std::unique(v.begin(), v.end()), v.end());
    
    i64 res = std::numeric_limits<i64>::max() / 4;
    int ans = 0;
    for (int k: v) {
        i64 sum = std::abs(calc(k));
        if (res > sum) {
            res = sum;
            ans = k;
        }
    }
    std::cout << ans << "\n";
    
    return 0;
}

Solution (faster)

上面的做法是我自己的,要两次排序去重,且中间还要不停 upper_bound,耗时太长,我们不妨延续 f ( x ) = f ( k ) + A ( x − k ) f(x) = f(k) + A(x - k) f(x)=f(k)+A(xk) 的思路,想一个更快的实现。

考虑 k = − 2 ⋅ 1 0 9 k = -2 \cdot 10^9 k=2109,显然 k ∉ a ∪ b k \notin a \cup b k/ab,且它的增量 A = 0 + m − n − 0 = m − n A = 0 + m - n - 0 = m - n A=0+mn0=mn。而当不断 k : = k + 1 k := k + 1 k:=k+1 直到第一个 k ∈ a ∪ b k \in a \cup b kab 时,

  • k = a i k = a_i k=ai,那么增量 A ′ = 1 + m − ( n − 1 ) − 0 = ( m − n ) + 2 A' = 1 + m - (n - 1) - 0 = (m - n) + 2 A=1+m(n1)0=(mn)+2
  • k = b j k = b_j k=bj,那么增量 A ′ = 0 + ( m − 1 ) − n − 1 = ( m − n ) − 2 A' = 0 + (m- 1) - n - 1 = (m - n) - 2 A=0+(m1)n1=(mn)2

经过一通手玩,我们发现,增量 A A A 每碰到一个 a i a_i ai + 2 +2 +2,每碰到一个 b j b_j bj − 2 -2 2。那我们可以把 a i , b j a_i, b_j ai,bj 都存到同一个数组里(请注意不需要去重),同时记录对应的小增量 + 2 +2 +2 − 2 -2 2

然后对于式子中的 f ( x ) f(x) f(x),它的含义就是:

  • + ( c a 1 ⋅ x   − ≤ x  的  a i  的和 ) + (c_{a_1} \cdot x \ - \leq x \ 的 \ a_i \ 的和) +(ca1x x  ai 的和)
  • − ( > x  的  a i  的和 − c a 2 ⋅ x ) - (> x \ 的 \ a_i \ 的和 - c_{a_2} \cdot x) (>x  ai 的和ca2x)
  • − ( c b 1 ⋅ x   − ≤ x  的  b j  的和 ) - (c_{b_1} \cdot x \ - \leq x \ 的 \ b_j \ 的和) (cb1x x  bj 的和)
  • + ( > x  的  b j  的和 − c b 2 ⋅ x ) + (> x \ 的 \ b_j \ 的和 - c_{b_2} \cdot x) +(>x  bj 的和cb2x)

由于我们把 x x x 前面的系数拎出来作为增量 A A A,所以我们只考虑后面那些 a i a_i ai b j b_j bj 的和,处理方式和 A A A 一样。

这样 f ( x ) f(x) f(x) 就是若干个一次函数的并,每一段都是 k x + b kx + b kx+b(注意这里 k k k 是斜率了,也就是上面的增量 A A A b b b 也不是数组 b b b,而是只考虑了后面那些 a i a_i ai b j b_j bj 的和),而我们要求的就是 min ⁡ ( ∣ k x + b ∣ ) \min(|kx + b|) min(kx+b),所以考虑最近的三个点 { − b k − 1 ,   − b k ,   − b k + 1 } \{ -\frac{b}{k} - 1, \ -\frac{b}{k}, \ -\frac{b}{k} + 1 \} {kb1, kb, kb+1} 即可。

C++ Code (faster implementation)

#include <bits/stdc++.h>

using i64 = long long;

constexpr int inf = 2E9;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n, m;
    std::cin >> n >> m;

    int k = m - n;
    std::vector<std::pair<int, int>> v;
    v.reserve(n + m + 1);

    i64 b = 0;
    for (int i = 0; i < n; i++) {
        int x;
        std::cin >> x;
        v.emplace_back(x, 2);
        b += x;
    }
    for (int i = 0; i < m; i++) {
        int x;
        std::cin >> x;
        v.emplace_back(x, -2);
        b -= x;
    }
    v.emplace_back(inf, 0);

    std::ranges::sort(v);

    int ans = -inf;
    i64 res = std::abs(static_cast<i64>(k) * ans + b);

    int l = -inf;
    for (const auto &[r, add]: v) {
        if (k != 0) {
            i64 c1 = -b / k;
            for (int i = -1; i <= 1; i++) {
                i64 c2 = c1 + i;
                c2 = std::max<i64>(c2, l);
                c2 = std::min<i64>(c2, r);
                i64 sum = std::abs(k * c2 + b);
                if (res > sum) {
                    res = sum;
                    ans = c2;
                }
            }
        }
        k += add;
        b -= static_cast<i64>(add) * r;
        l = r;
    }
    std::cout << ans << "\n";
    
    return 0;
}
### 关于牛客小白109的信息 目前并未找到关于牛客小白109的具体比信息或题解内容[^5]。然而,可以推测该事可能属于牛客网举办的系列算法之一,通常这类比会涉及数据结构、动态规划、图论等经典算法问题。 如果要准备类似的事,可以通过分析其他场次的比题目来提升自己的能力。例如,在牛客小白13中,有一道与二叉树相关的题目,其核心在于处理树的操作以及统计最终的结果[^3]。通过研究此类问题的解决方法,能够帮助理解如何高效地设计算法并优化时间复杂度。 以下是基于已有经验的一个通用解决方案框架用于应对类似场景下的批量更新操作: ```python class TreeNode: def __init__(self, id): self.id = id self.weight = 0 self.children = [] def build_tree(n): nodes = [TreeNode(i) for i in range(1, n + 1)] for node in nodes: if 2 * node.id <= n: node.children.append(nodes[2 * node.id - 1]) if 2 * node.id + 1 <= n: node.children.append(nodes[2 * node.id]) return nodes[0] def apply_operations(root, operations, m): from collections import defaultdict counts = defaultdict(int) def update_subtree(node, delta): stack = [node] while stack: current = stack.pop() current.weight += delta counts[current.weight] += 1 for child in current.children: stack.append(child) def exclude_subtree(node, total_nodes, delta): nonlocal root stack = [(root, False)] # (current_node, visited) subtree_size = set() while stack: current, visited = stack.pop() if not visited and current != node: stack.append((current, True)) for child in current.children: stack.append((child, False)) elif visited or current == node: if current != node: subtree_size.add(current.id) all_ids = {i for i in range(1, total_nodes + 1)} outside_ids = all_ids.difference(subtree_size.union({node.id})) for idx in outside_ids: nodes[idx].weight += delta counts[nodes[idx].weight] += 1 global nodes nodes = {} queue = [root] while queue: curr = queue.pop(0) nodes[curr.id] = curr for c in curr.children: queue.append(c) for operation in operations: op_type, x = operation.split(' ') x = int(x) target_node = nodes.get(x, None) if not target_node: continue if op_type == '1': update_subtree(target_node, 1) elif op_type == '2' and target_node is not None: exclude_subtree(target_node, n, 1) elif op_type == '3': path_to_root = [] temp = target_node while temp: path_to_root.append(temp) if temp.id % 2 == 0: parent_id = temp.id // 2 else: parent_id = (temp.id - 1) // 2 if parent_id >= 1: temp = nodes[parent_id] else: break for p in path_to_root: p.weight += 1 counts[p.weight] += 1 elif op_type == '4': pass # Implement similarly to other cases. result = [counts[i] for i in range(m + 1)] return result ``` 上述代码片段展示了针对特定类型的树形结构及其操作的一种实现方式。尽管它并非直接对应小白109中的具体题目,但它提供了一个可借鉴的设计思路。 ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值