「Solution Set」JOISC 2022

发布时间 2023-06-21 21:13:02作者: cc0000

Day1 监狱

首先我们感性理解:每名囚犯一定是依次走到自己的目的地的。因为如果起点或终点挡着别人的路,让他先走到目的地就行了。而在中间的话还容易挡着别人的路。

所以如果一个人的起点在另一个人的路径上,那么这个人必须先走,如果一个人的终点在别人的路径上,那么这个人必须后走。

然后就随便用树剖加线段树维护一下就行。

为什么我几个月前写了一遍这个还没过当时却死活想不出来为什么明明这题很简单的啊然后我还跑到别人的博客下面问智障问题半年后感觉尴尬的要死

Submission

Day1 京都观光

我们观察一下一个情况

 l    r
i|____|
 |    |
j|____|
 |    |
k|____|

如果我们从 \(j\) 处拐弯走到 \((k,r)\),如果想要走的比从 \(i\) 拐弯,从 \(k\) 拐弯都要近的话,那我们列一下式子:

\(a_i(r-l)+b_r(k-i)>b_l(j-i)+a_j(r-l)+b_r(k-j)\)

\(a_k(r-l)+b_l(k-i)>b_l(j-i)+a_j(r-l)+b_r(k-j)\)

然后乱移一下项,得到这么个东西:\(\frac {a_i-a_j}{i-j}<\frac {b_l-b_r}{l-r}<\frac {a_j-a_k}{j-k}\)

你发现中间这项没有用,因为是跟纵向道路有关的。

所以:\(\frac {a_i-a_j}{i-j}<\frac {a_j-a_k}{j-k}\)

然后发现这是个像斜率的东西。如果我们把他们连起来,由于横坐标递增,那么如果前一段的斜率小于后一段,那么这个点就是无用的。也就是说,我们需要保证斜率单调递减,然后发现这应该是个凸包。

然后我们去掉了完全无用的道路。

横纵都去掉了之后我们再分析在一个点上,向左走还是向右走更优。

那么得到的就是 \(\frac {a_i-a_j}{i-j}>\frac {b_l-b_r}{l-r}\)

其实就是把两个斜率单调递增的凸包上的点合并成一个。可以用双指针随便写。

但仔细想就知道就是在图上贪心地走,怎么走更优就往哪边走。

Submission

Day1 错误拼写

乱画一下图就发现

如果要求 \(T_{A_j}\leq T_{B_j}\)

假设 \(A_j<B_j\),那么等价于要求从 \(A_j\)\(B_j\) 一开始必须是连续相同字符,然后第一个不同的位置要求前一个大于后一个。

反过来也是一样。

那么我们考虑设计这样一个 DP 方程:\(f_{i,j}\) 表示到 \(i\) 个位置,自己是字母 \(j\) 的方案数。

转移的时候从后向前转移。我们发现这些限制形成了两类的区间。我们假设 \(A_j<B_j\) 的是一类区间,否则是二类区间

先考虑暴力转移。我们可以枚举一个转移点 \(k\),从 \(i\)\(k-1\) 的所有字母相同,然后枚举 \(k\) 的位置上放什么字母。如果放的比原来的大,那么就需要考虑所有的一类区间,如果当前存在一类区间的 \(r\) 大于等于 \(k\),那么就一定不行。现在复杂度 \(O(n^2 c^2)\)\(c\) 是字符集大小)

所以我们考虑优化暴力。我们考虑将转移分开,分成变大或变小两种。我们先动态维护 \(g_i\) 表示先从字母 \(i\) 连续,然后下一个变大的方案数,也维护一个 \(h_i\) 就是下一个变小的方案数。然后这样可能会有多算的。仔细想一下,多算的只有可能是左端点在 \(i\) 上的。因为如果是别的位置的区间,那么前面的转移就已经排除掉了。所以我们对两种情况维护两个堆什么的,如果以前可以的转移点现在不行了,就直接取出来,然后减掉贡献,再出堆就行。

这样做可行的原因是变化的那个点一定是在 \(i\) 后的一段区间,而且如果在某个位置发现不行了,那么之后一定不行。

我说的好抽象啊。语文还是太差了。

Submission

Day2 团队竞技

好像可以当成巨大数据结构做。

如果三个值分别是最大值的人没有别的属性是最大值,那么就选他们三个就行了。否则那个有重复的老哥怎么选都不可能被选上。那么把他扔了就行。

然后这样重复排除人选,直到最大值的三个人满足条件。那么这三个人就一定行,不用继续找了。

Submission

Day2 复制粘贴 3

我们先考虑如果使用复制粘贴操作更好的话,那么最好的操作方式是 \(a_0+s+a_1+s+\dots +s+a_n\) 之类的,\(s\) 是重复粘贴的那个,而 \(a_i\) 是中间手敲的字符。

正常考虑 DP 的话发现它不一定是从前向后转移的所以前 \(i\) 个位置的那种 DP。

所以我们考虑区间 DP?

假设 \(f_{i,j}\) 表示 \(i\sim j\) 打出来的最小步数。

我们先设 \(g_{i,j}\) 表示 \(i\sim j\) 打出来,但是 \(i\) 必须是复制粘贴的那个。

再设 \(h_{i,j}\) 表示 \(i\sim j\) 打出来,但是 \(i\) 必须用来复制粘贴,\(j\) 是用来复制粘贴的串的末尾。

我们发现 \(g_{i,j}\) 能用 \(h_{i,j}\) 转移,\(f_{i,j}\) 能用 \(g_{i,j}\) 转移出来,然后我们只需要考虑 \(h_{i,j}\) 怎么求。

如果 \(f_{i,j}\) 已知,那么我们可以找到下一个不相交的相同的区间,然后转移到 \(h_{i,j}\)。因为我们找的是下一个不相交的,所以对于每个 \(i\),转移点总数是调和级数。

然后我们就知道了 \(h_{i,j}\) 了,可能对于每个 \(h_{i}\) 这一行,需要求一个前缀最小值之类的。

[Submission](