首页 > 其他分享 >【题解】P5574 [CmdOI2019]任务分配问题

【题解】P5574 [CmdOI2019]任务分配问题

时间:2022-12-31 10:33:53浏览次数:60  
标签:p3 p4 int 题解 任务分配 mid cost cr P5574

sto cmd 学长 orz

题意

P5574 [CmdOI2019]任务分配问题

给定一个长度为 \(n\) 的排列,试将它分成 \(k\) 段,使得每段的顺序对数量之和最小。

\(n \leq 2.5 \times 10^4, k \leq 25\)

思路

决策单调性优化 dp + 分治乱搞。

dp 做法是显然的:

令 \(f[i][j]\) 表示前 \(i\) 个数分成 \(j\) 段的最小代价。

令 \(c(i, j)\) 表示 \([i, j]\) 内的顺序对数量,则 \(f[i][j] = \min\limits_{k = 0}^{i - 1} f[k][j - 1] + c(k + 1, i)\)

这个方程满足四边形不等式,感性理解:

令 \(p1 \leq p2 \leq p3 \leq p4\),观察四边形不等式:

\(c(p1, p3) + c(p2, p4) \leq c(p1, p4) + c(p2, p3)\)

令 \(s(l, r, L, R)\) 表示左端点在 \([l, r]\) 且右端点在 \([L, R]\) 的顺序对数量,则:

\(c(p1, p4) - c(p1, p3) = s(p1, p3, p3 + 1, p4) + s(p3 + 1, p4, p3 + 1, p4)\),记为 \(\Delta1\)

\(c(p2, p4) - c(p2, p3) = s(p2, p3, p3 + 1, p4) + s(p3 + 1, p4, p3 + 1, p4)\),记为 \(\Delta2\)

那么 \(\Delta1 - \Delta2 = s(p1, p3, p3 + 1, p4) - s(p2, p3, p3 + 1, p4) \geq 0\)

所以四边形不等式成立。

观察发现是 2D/1D 类型的 dp,于是可以考虑单调性分治优化。

问题在于求出 \(c\).

区间数顺序对单次查询可以考虑用树状数组,区间做在不考虑复杂度的情况下可以莫队。然后——

因为分治树的人类智慧性质,直接在分治的时候跑类似莫队状物的复杂度是对的。

void solve(int l, int r, int L, int R)
{
    if (l > r) return;
    int mid = (l + r) >> 1;
    int cost = inf, pos = -1;
    for (int i = min(R, mid - 1); i >= L; i--)
    {
        modify(i + 1, mid);
        if (g[i] + cw < cost) cost = g[i] + cw, pos = i;
    }
    f[mid] = cost;
    solve(l, mid - 1, L, pos);
    solve(mid + 1, r, pos, R);
}

上面的代码中 modify(i + 1, mid); 代表将莫队的左右端点分别移动到 \(i + 1\) 和 \(mid\)。观察一下,你发现在这里的复杂度是区间长度乘以 \(\log\). 对于下面的递归,递归到左半的复杂度另算,递归到右半时指针会整体向右挪动一次,但是复杂度同上。

于是按普通单调性分治复杂度的证法,这里摊下来只是多一只 \(\log\) 的问题!!!1

时间复杂度 \(O(nk \log^2 n)\)

代码

#include <cstdio>
#include <cstring>
using namespace std;

const int maxn = 2.5e4 + 5;
const int maxk = 30;
const int inf = 0x3f3f3f3f;

inline int min(const int &a, const int &b) { return (a <= b ? a : b); }

int n, k;
int cl, cr, cw;
int a[maxn];
int f[maxn], g[maxn];

namespace BIT
{
    int c[maxn];

    int lowbit(int x) { return x & (-x); }

    void update(int p, int w) { for (int i = p; i <= n; i += lowbit(i)) c[i] += w; }

    int query(int p)
    {
        int res = 0;
        for (int i = p; i; i -= lowbit(i)) res += c[i];
        return res;
    }
}

void modify(int tl, int tr)
{
    while (cl > tl)
    {
        cw += (cr - cl + 1 - BIT::query(a[--cl]));
        BIT::update(a[cl], 1);
    }
    while (cr < tr)
    {
        cw += BIT::query(a[++cr] - 1);
        BIT::update(a[cr], 1);
    }
    while (cl < tl)
    {
        cw -= (cr - cl + 1 - BIT::query(a[cl++]));
        BIT::update(a[cl - 1], -1);
    }
    while (cr > tr)
    {
        cw -= BIT::query(a[cr--] - 1);
        BIT::update(a[cr + 1], -1);
    }
}

void solve(int l, int r, int L, int R)
{
    if (l > r) return;
    int mid = (l + r) >> 1;
    int cost = inf, pos = -1;
    for (int i = min(R, mid - 1); i >= L; i--)
    {
        modify(i + 1, mid);
        if (g[i] + cw < cost) cost = g[i] + cw, pos = i;
    }
    f[mid] = cost;
    solve(l, mid - 1, L, pos);
    solve(mid + 1, r, pos, R);
}

int main()
{
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for (int i = 1; i <= n; i++)
    {
        BIT::update(a[i], 1);
        g[i] = g[i - 1] + BIT::query(a[i] - 1);
    }
    memset(BIT::c, 0, (n + 1) * sizeof(int));
    if (k == 1)
    {
        printf("%d\n", g[n]);
        return 0;
    }
    cl = 1, cr = 0;
    for (int i = 2; i <= k; i++)
    {
        solve(i, n, i - 1, n);
        memcpy(g, f, (n + 1) * sizeof(int));
    }
    printf("%d\n", f[n]);
    return 0;
}

标签:p3,p4,int,题解,任务分配,mid,cost,cr,P5574
From: https://www.cnblogs.com/lingspace/p/p5574.html

相关文章

  • 【题解】P1912 [NOI2009] 诗人小G
    题意多测。给定\(n\)个字符串和一个常数\(L\),试将这些字符串分成若干组,使得:令\(len(i)\)为第\(i\)个字符串的长度,则每组字符串的\(|\sum\limitslen(i)-L|\)......
  • 【题解】P3158 [CQOI2011]放棋子
    兄弟们,我起了,一日之计在于晨呐。题意P3158[CQOI2011]放棋子有一个\(n\)行\(m\)列的棋盘和\(c\)种颜色的棋子,每种棋子有\(a_i\)个。要求不同颜色的棋子不能放......
  • HDU 6801 Game on a Circle 题解 (推式子,多项式)
    题目链接首先注意到我们对这个环的扫描是一轮一轮进行的,每轮都会从左到右对每个没被删除的元素以p的概率删除。如果我们能对每个\(t(t\in[0,\infin],t是整数)和i\)求出c......
  • Leetcode面试高频题分类刷题总结及题解(更新中)
    原文链接:Leetcode面试高频题分类刷题总结排序类(Sort)基础知识:快速排序(QuickSort),归并排序(MergeSort)的原理与代码实现。需要能讲明白代码中每一行的目的。快速排序时间......
  • 【题解】P3515 [POI2011]Lightning Conductor(二分栈/分治优化DP)
    [POI2011]LightningConductor题面翻译给定一个长度为\(n\)的序列\(\{a_n\}\),对于每个\(i\in[1,n]\),求出一个最小的非负整数\(p\),使得\(\forallj\in[1,n]\),都......
  • 本博客提供的题解仅供学习,请勿抄袭代码
    近日,发现有部分同学翻取博客上的题解程序复制提交的抄袭行为;在此声明:本博客的代码仅供大家学习参考,对于自身还未学习到对应知识点的同学,请先完善自身基础知识的学习,当且仅......
  • 洛谷P2296 [NOIP2014 提高组] 寻找道路 题解
    题目链接:P2296[NOIP2014提高组]寻找道路-洛谷|计算机科学教育新生态(luogu.com.cn)好了,话不多说上代码:1#include<bits/stdc++.h>2usingnamespacestd;3......
  • Ynoi2019模拟赛题解
    \(Ynoi2019\)模拟赛题解前言:第一次做\(Ynoi\)还是有被离谱到的,我从来没有做到过这么卡常的题目,我到现在\(T1\)都还是\(70\)分,\(lxl\)毒瘤名不虚传啊。但是不得不说,\(Ynoi......
  • 【题解】P1973 [NOI2011] NOI 嘉年华
    yyc学长说是典题,就记一下。题意给出\(n\)个区间,试在丢弃一些区间后,把区间分成两部分,使得不存在同时被两部分中的区间覆盖的位置,求:最终包含区间数较小的部分的区间......
  • 【题解】P1527 [国家集训队]矩阵乘法(整体二分)
    [国家集训队]矩阵乘法题目描述给你一个\(n\timesn\)的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第\(k\)小数。输入格式第一行有两个整数,分别表示矩阵大小\(n......