首页 > 其他分享 >D. Blocking Elements

D. Blocking Elements

时间:2024-01-31 19:22:38浏览次数:27  
标签:Elements int text LL mid elements array Blocking

D. Blocking Elements

You are given an array of numbers $a_1, a_2, \ldots, a_n$. Your task is to block some elements of the array in order to minimize its cost. Suppose you block the elements with indices $1 \leq b_1 < b_2 < \ldots < b_m \leq n$. Then the cost of the array is calculated as the maximum of:

  • the sum of the blocked elements, i.e., $a_{b_1} + a_{b_2} + \ldots + a_{b_m}$.
  • the maximum sum of the segments into which the array is divided when the blocked elements are removed. That is, the maximum sum of the following ($m + 1$) subarrays: [$1, b_1 − 1$], [$b_1 + 1, b_2 − 1$], [$\ldots$], [$b_{m−1} + 1, b_m - 1$], [$b_m + 1, n$] (the sum of numbers in a subarray of the form [$x,x − 1$] is considered to be $0$).

For example, if $n = 6$, the original array is [$1, 4, 5, 3, 3, 2$], and you block the elements at positions $2$ and $5$, then the cost of the array will be the maximum of the sum of the blocked elements ($4 + 3 = 7$) and the sums of the subarrays ($1$, $5 + 3 = 8$, $2$), which is $\max(7,1,8,2) = 8$.

You need to output the minimum cost of the array after blocking.

Input

The first line of the input contains a single integer $t$ ($1 \leq t \leq 30\,000$) — the number of queries.

Each test case consists of two lines. The first line contains an integer $n$ ($1 \leq n \leq 10^5$) — the length of the array $a$. The second line contains $n$ elements $a_1, a_2, \ldots, a_n$ ($1 \leq a_i \leq 10^9$) — the array $a$.

It is guaranteed that the sum of $n$ over all test cases does not exceed $10^5$.

Output

For each test case, output a single number — the minimum cost of blocking the array.

Example

input

3
6
1 4 5 3 3 2
5
1 2 3 4 5
6
4 1 6 3 10 7

output

7
5
11

Note

The first test case matches with the array from the statement. To obtain a cost of $7$, you need to block the elements at positions $2$ and $4$. In this case, the cost of the array is calculated as the maximum of:

  • the sum of the blocked elements, which is $a_2 + a_4 = 7$.
  • the maximum sum of the segments into which the array is divided when the blocked elements are removed, i.e., the maximum of $a_1$, $a_3$, $a_5 + a_6 = \max(1,5,5) = 5$.

So the cost is $\max(7,5) = 7$.

In the second test case, you can block the elements at positions $1$ and $4$.

In the third test case, to obtain the answer $11$, you can block the elements at positions $2$ and $5$. There are other ways to get this answer, for example, blocking positions $4$ and $6$.

 

解题思路

  显然答案具有二段性,即如果存在一种合法方案使得分割点总和以及每个分割段的和都不超过 $x$,那么大于 $x$ 的值也可以作为一个合法的答案,为此需要二分出最小的 $x$。

  对于二分值 $\text{mid}$ 则需要判断是否存在合法的分割方案。由于选择分割点相当于选择总和不超过 $\text{mid}$ 的子序列,所以可以尝试 dp 找到使得每个分割段的和都不超过 $\text{mid}$ 时分割点总和的最小值是多少,如果不超过 $\text{mid}$ 则说明存在分割方案。

  定义 $f(i)$ 表示将前 $i$ 个元素进行分割,且分割点总和以及每个分割段的和都不超过 $\text{mid}$ 的所有方案中,分割点总和的最小值。根据第 $i$ 个元素是否为分割点进行状态划分:

  • 如果 $a_i$ 是分割点,那么状态转移方程为 $f(i) = f(i-1) + a_i$。
  • 如果 $a_i$ 不是分割点,那么找到前一个分割点 $a_j$,要求满足最后一个分割段的和不超过 $\text{mid}$,即 $s_i - s_j \leq \text{mid}$,其中 $s_i = \sum_{k=1}^{i}{a_i}$ 表示前缀和。状态转移方程为 $f(i) = \min\limits_{s_i - s_j \leq \text{mid}}\left\{ f(j-1) + a_j \right\}$。

  其中定义 $f(-1) = f(0) = 0$,$a_0 = 0$。

  显然第二个部分需要优化,注意到本质就是在 $j \in [0, i-1]$ 中找到所有满足 $s_j \geq s_i - \text{mid}$ 的 $j$,然后对 $f(j-1) + a_j$ 取最小值。所以可以用值域线段树来优化,在每个 $s_i$ 处存储 $f(i-1) + a_i$ 并维护最小值,询问就相当于求以 $s_i - \text{mid}$ 为左端点的后缀的最小值。需要对 $s_i$ 和 $s_i - \text{mid}$ 离散化。

  最后如果 $f(n) \leq \text{mid}$ 则说明存在分割方案。

  AC 代码如下,时间复杂度为 $O\left({n \log{n} \cdot \log{\sum{a_i}}}\right)$:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 2e5 + 10;
const LL INF = 0x3f3f3f3f3f3f3f3f;

int n;
int a[N];
LL s[N];
LL xs[N], sz;
LL f[N];
struct Node {
    int l, r;
    LL mn;
}tr[N * 4];

int find(LL x) {
    int l = 1, r = sz;
    while (l < r) {
        int mid = l + r >> 1;
        if (xs[mid] >= x) r = mid;
        else l = mid + 1;
    }
    return l;
}

void build(int u, int l, int r) {
    tr[u] = {l, r, INF};
    if (l != r) {
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
    }
}

void modify(int u, int x, LL c) {
    if (tr[u].l == tr[u].r) {
        tr[u].mn = min(tr[u].mn, c);
    }
    else {
        int mid = tr[u].l + tr[u].r >> 1;
        if (x <= mid) modify(u << 1, x, c);
        else modify(u << 1 | 1, x, c);
        tr[u].mn = min(tr[u << 1].mn, tr[u << 1 | 1].mn);
    }
}

LL query(int u, int l, int r) {
    if (tr[u].l >= l && tr[u].r <= r) return tr[u].mn;
    int mid = tr[u].l + tr[u].r >> 1;
    LL ret = INF;
    if (l <= mid) ret = query(u << 1, l, r);
    if (r >= mid + 1) ret = min(ret, query(u << 1 | 1, l, r));
    return ret;
}

bool check(LL mid) {
    sz = 0;
    for (int i = 0; i <= n; i++) {
        xs[++sz] = s[i];
        xs[++sz] = s[i] - mid;
    }
    sort(xs + 1, xs + sz + 1);
    sz = unique(xs + 1, xs + sz + 1) - xs - 1;
    build(1, 1, sz);
    memset(f, 0x3f, n + 10 << 3);
    f[0] = 0;
    for (int i = 1; i <= n; i++) {
        modify(1, find(s[i - 1]), f[max(0, i - 2)] + a[i - 1]);
        f[i] = min(f[i - 1] + a[i], query(1, find(s[i] - mid), sz));
    }
    return f[n] <= mid;
}

void solve() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", a + i);
        s[i] = s[i - 1] + a[i];
    }
    LL l = 1, r = 1e14;
    while (l < r) {
        LL mid = l + r >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    printf("%lld\n", l);
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        solve();
    }
    
    return 0;
}

  比赛的时候我是用上面的做法过的,还可以用单调队列来优化。注意到 $s_j \geq s_i - \text{mid}$ 中的 $s_i - \text{mid}$,随着 $i$ 的增加 $s_i$ 也会增加,因此 $s_i - \text{mid}$ 也会增加,具有单调性。因此可以用单调队列来维护 $j \in [0, i-1]$ 中 $f(j-1) + a_j$ 的最小值。当枚举到 $i$,如果对头元素对应的 $s_j < s_i - \text{mid}$ 则弹出。

  AC 代码如下,时间复杂度为 $O\left({n\cdot \log{\sum{a_i}}}\right)$:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 2e5 + 10;

int n;
int a[N];
LL s[N];
LL f[N];
int q[N], hh, tt;

bool check(LL mid) {
    memset(f, 0x3f, n + 10 << 3);
    f[0] = 0;
    hh = 0, tt = -1;
    for (int i = 1; i <= n; i++) {
        // 把f(j-1)+a[j]加入单调队列中
        while (hh <= tt && f[max(0, q[tt] - 1)] + a[q[tt]] >= f[max(0, i - 2)] + a[i - 1]) {
            tt--;
        }
        q[++tt] = i - 1;    // 队列中存的是下标
        while (hh <= tt && s[q[hh]] < s[i] - mid) {    // 把队头不满足s[j]>=s[i]-mid的元素删除
            hh++;
        }
        if (hh <= tt) f[i] = min(f[i - 1] + a[i], f[max(0, q[hh] - 1)] + a[q[hh]]);
    }
    return f[n] <= mid;
}

void solve() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", a + i);
        s[i] = s[i - 1] + a[i];
    }
    LL l = 1, r = 1e14;
    while (l < r) {
        LL mid = l + r >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    printf("%lld\n", l);
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        solve();
    }
    
    return 0;
}

 

参考资料

  Codeforces Round #922 (Div. 2) Editorial:https://codeforces.com/blog/entry/125300%E3%80%81

标签:Elements,int,text,LL,mid,elements,array,Blocking
From: https://www.cnblogs.com/onlyblues/p/17999932

相关文章

  • 记录一下 ArrayBlockingQueue 消息堆积的问题
    前言由于之前这个系统的日志记录是被领导要求写表的,在不影响系统性能的前提下,日志的入库操作肯定是要改成异步进行的,当时利用ArrayBlockingQueue+线程+AOP简单的去实现了一下,但是初版代码测试下来发现了一个很严重的问题,就是日志丢失的问题,本文由此而来。初步构思代码实现逻辑实......
  • CF1795F Blocking Chips
    题意给定一棵大小为\(n\)的树,有\(k\)个人,第\(i\)个人在节点\(a_i\)。从第\(1\)秒开始,依次操作第\(1,2,3,\ldots,k,1,2,3,\ldots,k,\ldots,k,\ldots\)个人,把这个人移动到没有走过的点。Sol调了\(3h\),给哥们整吐了。不难想到二分答案时间,算出每个人走......
  • Angular 17+ 高级教程 – Component 组件 の Query Elements
    前言Angular是MVVM框架。MVVM的宗旨是"不要直接操作DOM"。在 Component组件のTemplateBindingSyntax文章中,我们列举了一些常见的DOMManipulation。constelement=document.querySelector<HTMLElement>('.selector')!;element.textContent='value';......
  • CompletableFuture + LinkedBlockingDeque 实现生产者消费者案例
    设计要求:1.设计一个生产者生产,消费者消费场景;2.使用线程池 CompletableFuture+队列LinkedBlockingDeque实现;3.生产者生产的数据存储到长度为5的LinkedBlockingDeque队列,消费者消费从LinkedBlockingDeque队列中取数据;4.生产者和消费者均是多线程且不知道谁快谁慢,互......
  • Adobe Photoshop Elements 2024 v24.0 简体中文版 | 中文直装版
    下载:资源下载介绍:PhotoshopElements2024(简称PSE即PS简化版)是一款定位在数码摄影领域的全新的图像处理软件,该软件包括了专业版的大多数特性,只有少量的简化选项,提供了调整颜色和光线,去除划痕,修复旧照片,打开闭合的眼睛等实用功能,非常方便。除此之外,这款软件操作简单,使用方......
  • 16.What are the basic elements of an argument according to Toulmin Model? How do
    Round1:UnderstandingtheBasicElementsofToulminModelSpeaker1(StudentA):Hello,everyone!Let'sstartbydiscussingthebasicelementsoftheToulminModelofargumentation.AccordingtoToulmin,anargumentconsistsofthreemaincomponents......
  • PriorityBlockingQueue 优先级队列
    packagestudy;importlombok.Data;importjava.util.Comparator;importjava.util.concurrent.PriorityBlockingQueue;publicclassPriorityBlockingQueueDemo{publicstaticvoidmain(String[]args)throwsInterruptedException{PriorityBlocki......
  • 阻塞队列之 LinkedBlockingQueue
    LinkedBlockingQueue:Java多线程编程中的阻塞队列在Java多线程编程中,LinkedBlockingQueue是一个非常重要的类,它提供了一种用于在生产者和消费者之间进行数据传递的机制。LinkedBlockingQueue广泛应用于各种场景,如线程池、任务队列等。本文将详细介绍LinkedBlockingQueue的原理......
  • PostgreSQL - Check blocking SQL statements
    pg_locksviewLookingat pg_locks showsyouwhatlocksaregrantedandwhatprocessesarewaitingforlockstobeacquired.Agoodquerytostartlookingforlockproblems:selectrelation::regclass,*frompg_lockswherenotgranted;pg_stat_activit......
  • SAP Fiori Elements 针对 OData V2 和 V4 的 Extension API
    sap.suite.ui.generic.template.ListReport.extensionAPI.ExtensionAPI属于SAPFioriElements的早期版本,它基于SAPUI5框架构建,主要是针对ABAP环境下的ODataV2服务。sap.fe.templates.ListReport.ExtensionAPI是新的FiorielementsforODatav4的一部分,它是基于SA......