zoukankan      html  css  js  c++  java
  • 2021 ICPC Jinan D Arithmetic Sequence

    2021 ICPC Jinan D - Arithmetic Sequence

    题意:给出\(n\)个数,每一次操作都可以使一个数\(+1\)\(-1\),问最少需要多少次操作使得这些数形成一个等差数列。

    赛时想到了直线拟合,斜率三分找到最优解后再截距三分,很明显是不可行的,因为这两个局部最优是无法推出全局最优的。

    我们用\(f(d)\)表示当斜率为\(d\),也就是公差为\(k\)时需要的最少操作数,大胆猜想\(f(d)\)是个单峰函数。(事实证明是对的,就是证明懒得证了,要是真比赛估计就硬着头皮冲一发了)

    那么外面部分就可以用三分来实现了,也顺便更新了一发自己的三分写法。

    接下来我们只需要求出这个最少操作数就可以了。对于当前斜率\(d\),设首项为\(x\),每一项的贡献为\(|x+(i-1)\cdot d-a[i]|\),等价于\(|x-[a[i]-(i-1)\cdot d]|\)。转换成这个形式不难看出,问题变成了“对于数轴上任意的\(n\)个点,找到一个点\(x\),使得这个点到这\(n\)个点的距离和最短”,这个\(x\)就是\(n\)个数的中位数。这里用\(nth\_element(b+1,b+n/2+1,b+n+1)\),仅排序第\(n/2+1\)个元素,这样就可以找到中位数。(对于其余部分的排序是不一定的,但能够保证\(b[n/2+1]\)是这个区间第\(n/2+1\)大的,即中位数)然后就是一个\(\Omicron(n)\)的计算,计算中会爆\(ll\),得开\(\_\_int128\)

    三分边界条件为\(l=r\),因此最终\(f(l)\)即为答案。

    #include <bits/stdc++.h>
    #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
    #define ll long long
    #define pb push_back
    using namespace std;
    const int maxn = 2e5 + 10;
    ll a[maxn];
    ll b[maxn];
    int n;
    void print(__int128 x)
    {
        if (x > 9)
            print(x / 10);
        putchar(x % 10 + '0');
    }
    __int128 cal(ll d)
    {
        for (int i = 1; i <= n; i++)
        {
            b[i] = a[i] - (i - 1) * d;
        }
        nth_element(b + 1, b + n / 2 + 1, b + n + 1);
        ll x = b[n / 2 + 1];
        __int128 sum = 0;
        for (int i = 1; i <= n; i++)
        {
            sum += abs(x - b[i]);
        }
        return sum;
    }
    int main()
    {
        fast;
        cin >> n;
        for (int i = 1; i <= n; i++)
        {
            cin >> a[i];
        }
        ll l = -1e13, r = 1e13;
        while (l < r)
        {
    
            ll mid1 = l + (r - l) / 3;
            ll mid2 = r - (r - l) / 3;
            __int128 ans1, ans2;
            ans1 = cal(mid1);
            ans2 = cal(mid2);
            if (ans1 <= ans2)
            {
                r = mid2 - 1;
            }
            else
                l = mid1 + 1;
        }
        print(cal(l));
    }
    
  • 相关阅读:
    数组的复制
    ==与equals()区别
    构造器与方法
    数据类型及类型转换
    java标识符与命名规则
    多线程 总结
    局部变量与成员变量
    Java反射机制
    java的动态代理机制详解
    USB设备描述符
  • 原文地址:https://www.cnblogs.com/endlesskkk/p/15645031.html
Copyright © 2011-2022 走看看