首页 > 其他分享 >CF1295E Permutation Separation 题解 线段树优化dp

CF1295E Permutation Separation 题解 线段树优化dp

时间:2023-03-30 12:11:36浏览次数:51  
标签:val int 题解 元素 pos Separation 集合 ldots dp

题目链接:https://codeforces.com/problemset/problem/1295/E

题目大意:

将排列 \(p_1, p_2, \ldots, p_n\) 先分成 \(p_1, \ldots, p_k\) 与 \(p_{k+1}, \ldots, p_n\) 两个集合。

然后可以将元素从左边的集合移动到右边的集合,也可以将元素从右边的集合移动到左边的集合,移动 \(p_i\) 的代价是 \(a_i\)。

用最小的总代价使左边的集合的元素都小于右边的集合。

解题思路:

"All elements in the left set smaller than all elements in the right set" 在左边的集合中的所有元素都要小于在右边的集合中的所有元素

”在左边集合中的所有元素都要小于在右边的集合中的所有元素” 意味着存在一个数值 \(val\) 使得:

  • 所有在第一个集合中的元素数值都 \(\lt val\),同时
  • 所有在第二个集合中的元素数值都 \(\ge val\)。

所以可以考虑从 \(1\) 到 \(n+1\) 开一个扫描线,开扫描线的同时维护每一个前缀 \(pos\) 的信息。

对于每一个下标 \(pos\),我们用 \(t[pos]\) 表示:

初始的时候将 \([p_1, p_2, \ldots, p_{pos}]\) 划分到第一个集合,将 \([p_{pos+1}, \ldots, p_n]\) 划分到第二个集合,然后将第一个集合中所有小于 \(val\) 的元素移动到第二个集合,同时将第二个集合中所有大于等于 \(val\) 的元素移动到第一个集合所需的最小花费。

可以发现,总的花费(\(t[pos]\))等于所有满足:

  • \(i \le pos\) 且 \(p_i \ge val\),或者
  • \(i \gt pos\) 且 \(p_i \lt val\)

的 \(a_i\) 之和。

那么问题来了:如果我们将 \(val\) 的数值增加 \(1\) 会发生什么事情呢?

我们定义排列 \(p\) 中数值为 \(val\) 的元素下标为 \(k\)(即 \(p_k = val\))。则:

  • 对于每一个下标 \(pos \ge k\) 我们不需要将 \(p_k\) 从第一个集合移动到第二个集合,所以我们需要将 \(t[pos] -= a_k\);
  • 对于每一个下标 \(pos \lt k\) 我们也不需要将 \(p_k\) 从第二个集合移动到第一个集合了,所以我们需要将 \(t[pos] += a_k\)。

答案是 \(\min\limits_{1 \le pos < n}(t[pos])\) 。

这意味着我们需要对 \(t\) 数组执行两种类型的操作:

  1. 区间更新(加上一个值);
  2. 区间查询(查询最小值)。

我们可以使用线段树在扫描 \(val\) 的同时执行这些区间操作,时间复杂度为 \(O(n \log n)\)。

示例程序:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;

int n, p[maxn], q[maxn], a[maxn];
long long t[maxn], ans = 1ll<<60;
long long tr[maxn<<2], lazy[maxn<<2];

void push_up(int rt) {
    tr[rt] = min(tr[rt<<1], tr[rt<<1|1]);
}

void push_down(int rt) {
    if (lazy[rt]) {
        tr[rt<<1] += lazy[rt];
        tr[rt<<1|1] += lazy[rt];
        lazy[rt<<1] += lazy[rt];
        lazy[rt<<1|1] += lazy[rt];
        lazy[rt] = 0;
    }
}

#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1

void build(int l, int r, int rt) {
    if (l == r) {
        tr[rt] = t[l];
        return;
    }
    int mid = (l + r) / 2;
    build(lson), build(rson), push_up(rt);
}

void update(int L, int R, int val, int l, int r, int rt) {
    if (L <= l && r <= R) {
        tr[rt] += val;
        lazy[rt] += val;
        return;
    }
    push_down(rt);
    int mid = (l + r) / 2;
    if (L <= mid) update(L, R, val, lson);
    if (R > mid) update(L, R, val, rson);
    push_up(rt);
}

long long query(int L, int R, int l, int r, int rt) {
    if (L <= l && r <= R) return tr[rt];
    push_down(rt);
    int mid = (l + r) / 2;
    long long res = 1ll<<60;
    if (L <= mid) res = min(res, query(L, R, lson));
    if (R > mid) res = min(res, query(L, R, rson));
    return res;
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", p+i), q[p[i]] = i;
    for (int i = 1; i <= n; i++)
        scanf("%d", a+i);

    for (int i = 1; i <= n; i++)
        t[i] = a[i] + t[i-1], ans = min(ans, t[i]);

    build(1, n, 1);

    for (int val = 1; val <= n; val++) {
        int k = q[val];
        update(k, n, -a[k], 1, n, 1);
        if (k > 1) update(1, k-1, a[k], 1, n, 1);
        ans = min(ans, query(1, n-1, 1, n, 1));
    }
    printf("%lld\n", ans);
    return 0;
}

标签:val,int,题解,元素,pos,Separation,集合,ldots,dp
From: https://www.cnblogs.com/quanjun/p/17272112.html

相关文章

  • 【题解】[HEOI2013]SAO
    题目分析:考虑这是一个树形图,所以就先直接当作树来做。这个题其实就是让我们求解有多少种拓扑序而且题目中边方向的限制其实就是在限制拓扑序的前后,而一般这种题在设计\(......
  • 【题解】Codeforces Round 861(CF1808)A - E1
    我忘记了今天有阳间CF,所以就开打的很晚,所以只是说一下做法,代码实现....还是算了吧。但是我也看了,我的思路其他的人都有写,所以这个做法正确性没问题。A.LuckyNumbers题......
  • Python ThreadPoolExecutor的简单使用
    pythonThreadPoolExecutor的简单使用一、前言Python3.2以后,官方新增了concurrent.futures模块,该模块提供线程池ThreadPoolExecutor和进程池ProcessPoolExecutor。使用......
  • CF1009F 题解
    一、题目描述:给定一棵以 1 为根,n 个节点的树。设 d(u,x) 为 u的子树中到 u 距离为 x 的节点数。对于每个点,求一个最小的 k,使得 d(u,k) 最大。 二、......
  • CentOS7中远程连接数据库连不上的问题解决方法
      当远程连接数据库连接不起来时:可能原因:1.检查网络防火墙或其他安全设置是否阻止了连接  2.mysql服务是否启动,查看systemctlstatusmysql3.是否提前授权:......
  • CentOS7系统放行TCP/UDP端口教程
    在使用CentOS7操作系统时,您需要放行某些端口,以便应用程序能够正常运行。下面是如何放行TCP/UDP端口的步骤。步骤1:SSH连接服务器使用SSH方式连接服务器,如果您不知道如何SSH......
  • CF1809G prediction - dp - 组合数学 -
    题目链接:https://codeforces.com/contest/1809/problem/G题解:一道很强的dp首先翻译条件:predictable是什么意思?发现就是对每一个下标,前缀max和下一个位置至少差一个......
  • ABC291题解(D-G)
    ABC291D-FlipCardsSolution:考虑DP,定义状态\(F_{i,0}\)为第\(i\)张卡片正面朝上的方案数,\(F_{i,1}\)为第\(i\)张卡片背面朝上的方案数,每次check是否相同然后转移即可......
  • R语言Kmeans聚类、PAM、DBSCAN、AGNES、FDP、PSO粒子群聚类分析iris数据结果可视化比
    全文链接:http://tecdat.cn/?p=32007原文出处:拓端数据部落公众号本文以iris数据和模拟数据为例,帮助客户了比较R语言Kmeans聚类算法、PAM聚类算法、DBSCAN聚类算法、AGNE......
  • 使用SQLALCHEMY 出现warning的问题解决
    运行程序时出现错误:UserWarning:NeitherSQLALCHEMY_DATABASE_URInorSQLALCHEMY_BINDSisset.DefaultingSQLALCHEMY_DATABASE_URIto"sqlite:///:memory:".'Neithe......