构造/贪心性质tricks

杂项(暂未归类)

树状数组优化贪心:邻项交换得到单峰序列的最小交换次数

有一个排列,你每次可以交换相邻两项,问你最少要交换几次才能得到单峰序列(峰的左边单调递增,峰的右边单调递减)。

注意到:最小的数一定在最左边/最右边。分配完最小的数字后,次小的数字也一定在最左边/最右边。

因此我们有一种贪心:每次都把最小的数字放到最左边/最右边,取代价最小的一种执行。

答案就是 \(\sum\limits_{i=1}^{n} \min(\sum\limits_{j=1}^{i-1}[a_j>a_i],\sum\limits_{j=i+1}^{n}[a_j>a_i])\) 。可以用树状数组优化。

Dilworth 定理:偏序构建k分图

例题:有n个长度不同的线段(区间),把它们任意排布在一条数轴上(可重叠),要求任意 \(k\) 个区间的并的极左和极右端点的差值不小于 \(m\)

看起来很奇怪不是吗。我们先考虑 \(k=2\) 的情况。此时相当于是相邻两个区间受到了制约。推一下公式会发现答案等于 \(m(n-1)-s\) 其中 \(s\) 是这些区间中 \(n-2\) 个区间的长度和。显然有贪心做法。

那么,推广到 \(k>2\) ,可以把问题分解为数个 \(k=2\) 的部分(把区间分为 \(k-1\) 类,每类单独跑一个子问题)。

证明:可以把“两个区间满足制约”表示为一种关系 \(\preceq\) ,容易发现这是一种偏序。根据 Dilworth 定理 ,容易证明上述方法最优。(OI-wiki链接

证明方法2:这种方案一定可行。如果想让答案更小,只能分出 \(\ge k\) 类区间,但是如果这么做那敌人可以在每个类别中各取一个,答案不一定符合制约。因此至多分为 \(k\) 类。

树上:选出一定点使它们的 \(k\) 半径圆覆盖所有节点

显然,从叶子往上,能不选就不选,只有必须选的时候才选,这样是最优的。维护 \(f_u,g_u\) 分别表示 \(u\) 子树内最深的没被覆盖的点的深度和最浅的选择的点的深度,就可以处理子树间的覆盖了。

构造若干维的每种颜色连通的立体区域

3维的见CF1965E

任意交换问题

一般来说,交换问题是泛指你可以任意交换两个或多个位置上的数字(可能有些限制,但是不必相邻),求达到某种状态的最小交换次数/交换次数最少的一种方案。

这类问题通常要把原序列转化为一个图。每个数字都连一条边,指向“占据它应该去的地方的数字”。(如果原序列是一个排列那么再好不过了,因为这样建出来的图中只有纯环)如果我们的目的是把序列排序,那么我们只需要把环都拆成一个个点(自环)就行了。

一个简单的例题:给你一个排列,你每次可以选择 \(k\) 个数字任意排列,求最小操作次数。做法是建成由很多环组成的图,搜索每个环的大小,注意到每次操作可以消除 \(3\) 个点,而剩下的部分只有可能是大小为 \(2\) 的环,可以两两消除,所以答案是 \(\sum \frac{size_{环}}{3}+\lceil\frac{\sum[size_{环}\mod 3=2]}{2}\rceil\)

欧拉回路:构造一种交换方案(不必相邻)达到某个目标

例1:有一个序列要排序,你可以相邻/不相邻交换,且有些限制,请问你交换的最少次数。

例2:有 \(n\) 个初始是满的容器,每个容器有 \(m\) 个东西和位置,东西有 \(m\) 类,你要保证每个容器里每个物品恰有一个。你有一个空位可以存放一个东西,且一次操作只能把一个东西挪到空位(然后他自己空出来)。

这种题目一般就是欧拉回路(或者单纯的环,如例1)。

欧拉回路:从起点出发,经过若干条边,又回到起点,每条边只允许经过一次。

直接使用dfs可求欧拉回路 (据说并查集也可以)。这种问题的难点一般在想到可以建图这一点上。

多区间贪心(偏序)

经典的可使用排序法贪心解决的多区间问题。

有一个 \(\mathrm{0/1}\) 数组上有若干限制,每个限制要求区间内的和 \(\ge x\)。求整个数组的总和最小是多少。

将区间按照右端点从小到大排序,然后将如果总和不够的话就尽量把右边的点变为 \(1\)

同理也有区间和 \(\le x\) 的。可以通过反转整个数组中的0/1来做到。

拟阵与贪心

拟阵,简单来说就是你有一些元素和一些可行的选取方案,满足以下条件:

  • 遗传性:可行的选取方案的子集也是可行的方案。
  • 增长性:设两个可行的选取方案为 \(A,B\)\(|A|<|B|\)。对于所有 \(x\in B \wedge x\notin A\)\(A\bigcup x\) 也是可行选取方案。

拟阵具有凸性。详见北京市集训贪心部分。

贪心的要点

注意边界情况

注意:不等号取等的情况。如考试题

KMP 及其引申

众所周知,KMP 一种在 \(O(n+m)\) 复杂度内在匹配串中查找模式串的算法。

其关键在于维护一个 \(pre_i\)(或者叫 \(next_i\) )表示模式串前 \(i\) 位构成的子串中,最长的公共前后缀的长度。(字符串 \(s\) 的公共前后缀既是 \(s\) 的前缀也是 \(s\) 的后缀)

借由 \(pre_i\) ,我们可以求出 \(f_i\) 表示匹配串前 \(i\) 位能匹配到的最长模式串前缀。prei实际上代表如果在第i+1位失配,应该将指向模式串的指针指向哪里。

在 KMP 算法中,\(pre\) 数组和 \(f\) 都由相似的方法求出:初始令 \(p=pre_{i-1}\) ,不断令 \(p=pre_p\) ,直到满足条件。

复杂度

失配时,我们会把指向模式串的一直往前跳直到不失配,但是我们每跳一次最少会往前跳1位,因此我们模式串指针往前跳的次数不超过匹配串指针往后挪的次数。因此复杂度为 \(O(n+m)\)

灵活运用pre

可以使用pre快速解决的场景包括但不限于:

  • 查找一个字符串最多由多少相同的子串拼接而成
  • 查找最长的Q,使得Q是字符串A的前缀,且A是字符串QQ的前缀
  • 查找满足ABA格式的子串

等等。

灵活运用KMP

可能的应用场景:

  • 给定pre数组,求出原来字符串
  • 通过转化,把问题转变为“在一个数组内查询另一个模式串”

等等。

反悔贪心

反悔贪心就是贪心地选,但是未来可能推翻过去的贪心。

其明显的思维路径是:直接贪心可能会后悔,但是反悔的时候可以轻易确定要反悔什么、怎么反悔。

例题:

  • 建筑抢修类。以下条件三选二:任务有截止时间,任务有不同价值,任务有不同耗时。
  • 你只能拿一定容量的物品,但是物品只有一个参数,只是同一类别的数量有点多。考虑把一类物品捆绑销售。
  • 买冰棒/订货:作为典型例题,本是费用流例题,却可以用反悔贪心 \(O(n)\) 做到。简单来说就是从 \(n\)\(1\) 扫,如果可以把在后面买的冰棒放到现在来买就这么做。可以参考我的代码

反悔贪心一般要使用数据结构优化。有些看起来可以反悔的题目实际上要用二分做。

2025北京市集训后补:查看北京市集训“贪心”部分,其中「蔬菜」一题便利用了反悔贪心可被视为倒序的前缀匹配问题的特点。

构造型贪心

要求你构造出一种方案,使得答案最小。输出最小的答案。

例:基础构造练习题

这道题目看似是构造题,实际上确实是构造推结论贪心。

解法:

我大概想明白了,这里是一个子图---链的结构,但是链上可能有 1~2 个旁支。

显然 \(子图\times链\) 是差不多 \(x\times x\) 的,而如果从子树中拿出一部分来,作为旁支,那么节点数不会变,而 \(f(u)-f(v)\) 会变小(减小 \(2\times 旁支大小\times 旁支与子树距离\) )。

那么我们让 \(子树\times 链\)\(x\times x\) 或者 \(x\times(x+1)\) 的形式并且恰好 \(\ge\) 输入,只需要拿出一部分做旁枝,就能调整为目标,而答案不变。

请注意一次只能调整 \(2\) 的倍数,所以如果目标 \(f(u)-f(v)\) 是奇数,需要再多放一个旁支来调整,所以答案会 \(+1\) .

posted @ 2025-02-20 21:10  Luke_li  阅读(7)  评论(0)    收藏  举报