2024.7.1 之后的做题小记

7.1 P7124 [Ynoi2008] stcm

维护一个 \(O(n\log n)\) 级别的子树补不删除莫队。

Solution 1:

考虑菊花图,忽略根节点,一个显然的做法是把这些节点扔进线段树,然后遍历某个节点时候就把它的兄弟节点内所有点加进来。

这个做法是线段树所有节点大小和即 \(O(n\log n)\)

然后在一条链上做法简单,直接冲下去即可。

现在集合两种做法,重链直接冲,轻子树想办法用线段树合并一下。

不过考虑每颗子树大小不一样,直接建线段树,复杂度假假假。

改用合并果子式合并法建树,这叫 哈夫曼树(Huffman Tree),子树和小于 \(O(n\log n)\),复杂度真真真。

然后重链权值视为 \(1\),再拼接一下,就好了。

不过不好写,我是不想写的。

Solution 2:

想象拍平树,现在变成了序列上中间缺一节的不删除莫队。

看起来更困难,但是树上区间有一重要性质:任意两区间要么不交要么包含。

基于此得到一个分治做法:

\([l,r]\) 解决所有抠掉区间被它包含的询问。

只考虑包含 mid 的询问,不包含的直接递归处理。

然后你发现,所有包含 mid 的询问,由于上面那个性质,两个端点移动是单调的!

直接暴力移动即可,每轮 \(O(n)\),总共 \(O(n\log n)\),非常好写!!!

7.4 CF702F T-Shirts

\(n\) 种物品,每种有价格 \(c_i\),质量 \(q_i\)

这时候来了 \(m\) 个人,每个人手上有 \(v_i\) 块钱。

每个人会反复在自己买得起的东西里选取质量最高的买,有多件则找最便宜的。

每种物品有无数个,但是每个人每种只买一个(即人之间独立)。

现在询问每个人各能买到多少物品。

\(n,m\le 2\times 10^5\)\(c,q,v\le 10^9\)

Solution:

思考在线做法,发现有点困难,那先离线。

人按 \(v\) 排序,物品按 \(q\) 降序排。

现在变为每次用一个物品被许多人买,准确来说,让所有 \(v\ge c\) 的人 v-=c

这个问题有两种搞法。

法 1:平衡树维护(我不确定值域线段树行不行)

每次给 \(v\ge c\) 的打标记就行了……吗?

考虑有一部分 -=c 之后就会跟前面没减的顺序打乱,怎么办?

这部分显然是 \(c\ge v\ge 2c\),大于 \(2c\) 的直接打标记。

而这一部分咋办,直接暴力重构。

考虑每个点重构一次大小至少减半,故总的重构次数仅为 \(O(n\log v)\),复杂度为 \(O(n \log v \log n)\)

法 2:二进制分组

考虑到我不会写平衡树,而且连值域线段树都懒得写,就有了这个做法。

我们先按 \(v\) 分组,第 \(i\) 组保存 \(v∈[2^{i-1},2^i-1]\),然后考虑每个物品,显然是小的一些组全 \(<c\),直接跳过。

有一个组既有大的又有小的,这里考虑所有大的能减,而且减完必然跳组,故暴力跳。

大的一些组中有最小的一部分减完要跳组,这个直接暴力跳,剩下的整组打标记。

考虑我们要找到一组中最大以及最小的一批,使用值域线段树或者 set<int> 都可以。

复杂度等于总共跳的次数乘以 set 单次操作的复杂度,也是 \(O(n \log v \log n)\)

易错点:

  1. 如果 TLE on #55 考虑有大有小的那个组,为了复杂度的正确,我们必须每一个找到的元素都跳了,故这里要从大到小找,有不符合的后面直接 break;

  2. 改正之后如果 WA on #5 考虑前面实现出问题了,使得有些你认为跳了组的没跳出去,复杂度上无伤大雅,但是迭代器就可能会第二次指到它导致一个人买了两次同一件物品。

故这里改为最后统一结束后再插入。

7.5 CF1919F Wine Factory

\(n\) 个机器,每个机器有参数 \(a_i,b_i,c_i\)

现在,每个机器里初始有 \(a_i\) 升水,从第一个机器开始,每个机器会取 \(b_i\) 升水(不够就全取)酿酒,如果剩下了水,再运送 \(c_i\) 升水(不够就全运走)到下一个机器里

问最后所有机器总共酿多少酒,并且还有 \(q\) 次单点修改,每次修改后需要重新回答。

\(n,q\le 5\times 10^5\)\(a,b\le 10^9\)\(c\le 10^18\)。(\(c=10^18\) in easy version)

Solution:

非常好模拟网络流题目,例题级别的。

先说 easy version,此时显然就是没有向后流量限制。

然后假设全酿满,发现达不到这个条件是因为有时候水量 \(<b_i\)

那么直觉告诉我:酿不到的部分就是 \(b_i-a_i\) 的前缀最大值。

因为前面部分肯定至少有这么多搞不出来,而且此时这里空了对后面没有影响,后面不再出现水不够用的情况了(否则最大值会在后面的)

那么直接线段树维护即可。

再说 hard version,出现了流量限制考虑网络流建模。

连这样三种边:

  1. \(S→i,a_i\)
  2. \(i→T,b_i\)
  3. \(i→i+1,c_i\)

求这个玩意的最大流。

增广不好找性质,改为求最小割。

然后发现一个重要性质:\(S→i\)\(i→T\) 只会割一个。

证明:

显然不可能一个不割掉,这样直接流过去了。

没有必要割两个,因为:

  1. 如果存在其他路径从 \(S\)\(i\),那么 \(S→i\) 不必割掉。

  2. 如果不存在,\(i→T\) 不必割掉。

那么现在容易一些了:

对于 \(c_i\),要割掉当且仅当 \(i\) 割掉 \(b_i\)\(i+1\) 割掉 \(a_i\)

那么此时直接线段树维护,分四类讨论 \(l,r\) 各自割掉了啥就可以合并了。

7.8 CF121E Lucky Array

给定一个长为 \(n\) 的序列,进行 \(q\) 次操作,有两种操作:

  1. 区间加(只加正数)

  2. 求区间内幸运数数量,幸运数定义为只由 \(4,7\) 组成的数。

\(n,m\le 10^5\),保证序列中的数任意时刻总 \(\le 10^4\).

Solution:

后面这句话十分重要!

因为观察发现 \(10^4\) 内幸运数只有 \(30\) 个,可以枚举。

此时出现两个方法:

1. 分块

直接给序列分块,每个块开一个值域数组,然后修改时候散块暴力修改(并改值域数组),整块打标记;查询时候散块暴力验证,整块在值域数组上累加。

复杂度 \(O(n+m\sqrt{n}P)\),其中 \(P\)\(10^4\) 内幸运数个数 \(30\)

2. 线段树

按照类似倍增值域分块的思想,因为这里有三十个幸运数,我们就开三十个线段树。

然后每棵线段树里维护正好大于上一个幸运数小于等于这个幸运数的数。

修改同普通线段树,但是一旦超出范围就暴力重构,由于总的重构次数有限,复杂度均摊意义下正确。

(这个是口胡的,没写)

Fun_Strawberry 发现自己当时写的那个做题小记后来再也没有更新过,觉得不好,认为后面应该加快博客更新频率。

于是做题小记被复辟了。

7.25 CF1198D & E Rectangle Painting 1 & 2

1:给定一个 \(n\times n\) 矩阵,每个格子有黑白中一种颜色,每次花 \(max(n,m)\) 代价染白一个 \(n\times m\) 矩形,求全部染白最小代价。\(n\le 50\)

Solution:本来是想发掘一下性质,但是发现数据范围后,这题就是二位区间 DP 板子了。

是的区间 DP 可以二位,和一维差不多。

注意到每个矩形只有两种情况:

  1. 一个矩形全涂满。

  2. 分成独立的两个子矩形,否则绝对不优。

那么直接枚举分界线转移,复杂度达到 \(n^5\),反正过了。

2:给定一个 \(n\times n\) 矩阵,初始全白,现在有 \(m\) 个给定矩形区域(大小不限)染黑了,每次花 \(max(n,m)\) 代价染白一个 \(n\times m\) 矩形,求全部染白最小代价。\(m\le 50,n\le 10^9\)

不同于 \(\max\),如果那里是 \(\min\) 的话,每次染一整行或者一整列显然最优。

那么一个矩阵被染完,则必然它的所有行或所有列都被染过。

这个“或”很灵性,启示我们搞最小割。

首先行列离散化,我个人的离散化方法是,每个数本身算一行,每两个数之间的间隙(不管有没有)算一行,避免了边界上不知道间隙算不算的问题。

然后源点连向所有行,汇点连向所有列,边权为离散化前占的行/列数。

对于每一个矩形,连向包含它的所有行点和列点就行了,然后跑最小割就是答案。

posted @ 2024-07-01 16:50  Fun_Strawberry  阅读(36)  评论(0)    收藏  举报