首页 > 其他分享 >BalticOI 2004 Sequence 题解

BalticOI 2004 Sequence 题解

时间:2023-01-05 23:23:38浏览次数:69  
标签:... dist Sequence int 题解 个数 中位数 cost 2004

题目链接在这里~
对于序列\(\{a\}\),把每一个\(a_i\)减去一个\(i\),得到\(\{a'\}\)序列\(\{b\}\)同理。
因为\(b_1<b_2<...<b_n\),故\(b_1'\leqslant b_2'\leqslant ... \leqslant b_n'\)
又\(a_i-b_i\)不变,故新问题与原问题等价。
因此我们就把问题就转化成了一个单调不下降序列。

我们将\(\{a_1, a_2, ..., a_m\}\)序列分成两段\(a_1...a_n\)\(a_{n+1}...a_m\)
假设\(b_1=b_2=...=b_n=u,b_{n+1}=b_{n+2}=... = b_{m}=v\)

则这个问题就变成了经典的邮递员问题,即货仓选址问题。

分类讨论:

  1. \(u\leqslant v\),则前半段取\(u\),后半段取\(v\)即为最优解。
  2. \(u > v\),则\(\frac{u+v}{2}\)(\(u,v\)的中位数)为最优解,用左偏树实现。
    得到了中位数之后还需要下压.
    那么其中有两种情况. 奇数个数的中位数, 和偶数个数的中位数(为了方便用cost表示a和b的差值的绝对值):
    1 奇数个数的中位数的时候就只能选这个中位数, 然后无论再往上或者往下压的cost都得增加至少1. 那么如果这个中位数的解如果不小于再往前一段的解的话, 则可以结束. 若小于, 则需要再往前合并找中位数循环直到结束
    2 偶数个数的中位数. 那么可以在中间2个数之间上下浮动:
    a. 如果再往前一段的解在这2个数之间, 则取再往前一段的解即可. 否则如果往上虽然不增加cost但是还能下压, 往下的话就得继续合并前一段增加cost.
    b. 如果再往前一段的解小于这个区间, 则直接选这2个数里面小的即可
    c. 如果再往前一段的解大于这个区间, 则还是选这2个数里面小的, 然后再往之前合并

code

#include <bits/stdc++.h>

typedef long long ll;

using namespace std;

const int N = 1e6 + 10;

int n, v[N], dist[N], l[N], r[N], ans[N], tt;
ll res;

struct Segment{int end, root, size;}stk[N];

int merge(int x, int y)
{
    if (!x || !y) return x + y;
    if (v[x] < v[y]) swap(x, y);
    r[x] = merge(r[x], y);
    if (dist[r[x]] > dist[l[x]]) swap(r[x], l[x]);
    dist[x] = dist[r[x]] + 1;
    return x;
}

int pop(int x){return merge(l[x], r[x]);}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++ )
    {
        scanf("%d", &v[i]);
        v[i] -= i;
    }
    
    for (int i = 1; i <= n; i ++ )
    {
        auto cur = Segment({i, i, 1});
        dist[i] = 1;
        while (tt && v[cur.root] < v[stk[tt].root])
        {
            cur.root = merge(cur.root, stk[tt].root);
            if (cur.size % 2 && stk[tt].size % 2) cur.root = pop(cur.root);
            cur.size += stk[tt].size, tt -- ;
        }
        stk[ ++ tt] = cur;
    }

    for (int i = 1, j = 1; i <= tt; i ++ ) while (j <= stk[i].end)  ans[j ++ ] = v[stk[i].root];

    for (int i = 1; i <= n; i ++ ) res += abs(v[i] - ans[i]);
    printf("%lld\n", res);
    for (int i = 1; i <= n; i ++ ) printf("%d ", ans[i] + i);

    return 0;
}

标签:...,dist,Sequence,int,题解,个数,中位数,cost,2004
From: https://www.cnblogs.com/sunruize/p/17029107.html

相关文章

  • ATC简单题解(不定时更新
    ABC129前三题略D.lamp虽然数据范围不大,但也没法暴力check,可以考虑分别维护每行(每列)障碍物的纵(横)坐标,可以考虑到插入std::vector中,然后对于每一个点查找横竖方向上的......
  • CF893D Credit Card 题解
    简要题意:你有一张信用卡,\(n\)天有\(n\)个操作,每次操作给定一个\(x\),如果\(x\)是\(0\)那么银行会查询信用卡里的金额,要保证金额是非负数;否则你卡里的金额会变化\(x......
  • 安徽农业大学寒假训练赛1题解
    D#include<bits/stdc++.h>usingnamespacestd;charg[10][10];intmain(){for(inti=1;i<=5;i++)scanf("%s",g[i]+1);//这样可以保证下标都从1......
  • 题解 : Luogu P2197 【模板】nim 游戏
    题目链接:link结论如果$a_1\oplusa_2\oplus...\oplusa_n\not=0$,则先手必胜证明操作到最后时,每堆石子数都是\(0\),\(0\oplus0\oplus...\oplus0=0......
  • Starling常见问题解决办法
    ​​Starling常见问题解决办法​​来自:​​智慧+毅力=无所不能​​ 1、Android设备上阻止用户按下后退后的行为侦听按键事件//阻止后退行为view.stage.addEventL......
  • JSOI2009 题解
    Count二维树状数组板子题,开\(c\)个二维树状数组即可过。可以通过离线对每个权值单独操作做到只开一个二维树状数组。如果空间要求更紧的话可以cdq分治做到只开一个......
  • unity和VS2019联调问题解决
    以前使用VS2015和17的时候联调的时候是可以附加到unity进行联调的,今天用的2019发现不可以了。研究了一下是少装了一个插件。装上插件就解决了。过程如下:当前使用VS版本2019......
  • 洛谷 p5536 题解
    题目链接:https://www.luogu.com.cn/problem/P5536此题为树的dfs的一个应用。思路树dfs时,可以计算每个点的深度。如图所示可以多次dfs,从而找到不同的信息。代码......
  • CF513F2 题解
    题意传送门有\(a+b+1\)个会动的棋子,在一个大小为\(n\timesm\)的棋盘上,棋盘上有一些点有障碍。棋子中,有\(a\)个红色棋子,\(b\)个蓝色棋子,和\(1\)个既能当作红棋......
  • Codeforces Round #839 (Div. 3)题解
    C.DifferentDifferences(贪心)题意​ 给定\(k\),\(n\)\((2\lek\len\le40)\)。从\([1-n]\)中不重复地任选\(k\)个数组成一个数组,使这个数组的差分数组中不同的......