zoukankan      html  css  js  c++  java
  • [hihoCoder 1384]Genius ACM

    Description

    题库链接

    给定一个整数 (M),对于任意一个整数集合 (S),定义“校验值”如下:

    从集合 (S) 中取出 (M) 对数(即 (2 imes M) 个数,不能重复使用集合中的数,如果 (S) 中的整数不够 (M) 对,则取到不能取为止),使得“每对数的差的平方”之和最大,这个最大值就称为集合 (S) 的“校验值”。

    现在给定一个长度为 (N) 的数列 (A) 以及一个整数 (k)。我们要把 (A) 分成若干段,使得每一段的“校验值”都不超过 (k)。求最少需要分成几段。 多测,测试组数为 (T)

    (Tleq 12,1leq n,mleq 5 imes 10^5,0leq kleq 10^{18},0leq P_ileq 2^{20})

    Solution

    先考虑如何求“校验值”。思路是当题述结果最大时,应该要求最大值和最小值一组;次大值和次小值一组……

    我们以 (4) 个数为例,如 (a,b,c,d),其中 (aleq bleq cleq d) 。那么 ((d-a)^2+(c-b)^2=a^2+b^2+c^2+d^2-2ad-2bc)((b-a)^2+(d-c)^2=a^2+b^2+c^2+d^2-2ab-2cd)。容易发现 (ad+bc<ab+cd),故前者更优。更多数时用数学归纳法可以证明。

    其次,另外一个要点是当一个区间左端点固定时右端点要尽可能往右取。

    注意到这两点,我们左端点从 (1) 开始,向右找到最远的符合条件的右端点,划分为一段。再接着固定左端点,继续寻找。这样即可统计出答案。假设我们右端点是枚举得到的,记答案(最少段数)为 (ans),那么本算法的复杂度约为 (Oleft(ans imesleft(frac{n}{ans} ight)^2logfrac{n}{ans} ight)=Oleft(n^2 imesfrac{logfrac{n}{ans}}{ans} ight))

    若答案趋为 (1),复杂度趋为 (Oleft(n^2log n ight))。显然枚举右端点的思路是过不了此题的。而计算“校验值”的复杂度是无法再优化的,考虑如何快速寻找右端点。

    一个思路是二分右端点,其余同上,可以证明复杂度是 (O(nlog^2 n)) 的。不过可惜的是出题人把这个算法卡掉了。

    既然二分不行,我们考虑用倍增的思路来求右端点,具体思路是首先设倍增 (len) 长度为 1,若右端点为 (r),判断若端点 (r+len) 符合条件。则将右端点赋值为 (r+len) 并且将 (len) 倍增,继续讨论;若不符合条件则将 (len) 缩短一半,继续讨论。其实该算法的复杂度与二分是一致的,不过只能写倍增才能过此题。

    另外,不论二分还是倍增,排序直接 ( ext{sort}) 依旧过不了。注意到这样一个小技巧,如果上一个枚举的右端点为 (r),这一次右端点为 (r'),那么可以知道区间 ((l,r)) 是有序的,那么我们只需排序 ((r,r')) 的区间,之后将两个部分归并即可使整个 ((l,r')) 有序。

    Code

    #include <bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int N = 5e5+5;
    void gi(int &x) {
        x = 0; char ch = getchar();
        while (ch < '0' || ch > '9') ch = getchar();
        while (ch >= '0' && ch <= '9') x = x*10+ch-'0', ch = getchar();
    }
    
    int t, n, m, p[N], a[N], kl, kr, tmp[N];
    ll k;
    
    void merge(int l, int r, int x, int y) {
        int i = l, j = x, k = l;
        while (i <= r && j <= y)
            if (a[i] < a[j]) tmp[k++] = a[i++];
            else tmp[k++] = a[j++];
        while (i <= r) tmp[k++] = a[i++];
        while (j <= y) tmp[k++] = a[j++];
        for (int i = l; i <= y; i++) a[i] = tmp[i];
    }
    bool judge(int l, int r) {
        if (kl == l && r > kr) {
            for (int i = kr+1; i <= r; i++) a[i] = p[i];
            sort(a+kr+1, a+r+1);
            merge(l, kr, kr+1, r); kr = r;
        } else {
            for (int i = l; i <= r; i++) a[i] = p[i];
            sort(a+l, a+r+1);
            kl = l, kr = r;
        }
        int t = 0; ll cnt = 0;
        while (t < m && l < r) {
            cnt += 1ll*(a[r]-a[l])*(a[r]-a[l]);
            ++l, ++t, --r;
            if (cnt > k) return false;
        }
        return cnt <= k;
    }
    void work() {
        scanf("%d%d%lld", &n, &m, &k);
        for (int i = 1; i <= n; i++) gi(p[i]);
        int l = 1, r = 1, ans = 0, len, k;
        while (l <= n) {
            len = 1, k = l;
            while (len) {
                if (r+len <= n && judge(l, r+len)) {
                    r += len;
                    k = r, len <<= 1;
                }
                else len >>= 1;
            }
            l = r = k+1, ++ans;
        }
        printf("%d
    ", ans);
    }
    int main() {
        scanf("%d", &t);
        while (t--) work();
        return 0;
    }
  • 相关阅读:
    PySpark SQL 相关知识介绍
    Spark的安装及配置
    Hive的安装及配置
    Hadoop分布式集群的搭建
    寻找子串
    CPU呈现正弦函数曲线
    2.2链表中倒数第k个结点
    #137 Single Number II
    1.8字符串- 翻转子串
    1.7数组-清除行列
  • 原文地址:https://www.cnblogs.com/NaVi-Awson/p/11167203.html
Copyright © 2011-2022 走看看