JOISC 2015 题解

JOISC 2015

loj 上有几乎全部的题目,写了题意的就是 loj 上没有的。

D1T1

简单题。

因为 \(k\) 很小,考虑依次确定最后第 \(i\) 位是什么。我们倒序考虑所有操作,维护最后第 \(i\) 位当前在第几位,就可以一直推下去。

提交记录

D1T2

直接暴力复杂度就是 \(O(k4^k)\) 的,可以通过。

提交记录

D1T3

怎么直接加难度了啊。

考虑枚举最后保留的最大值的位置,这样两边可以分别计算贡献。

以左侧为例,我们可以设计 DP,设 \(f[i]\) 表示最后一个保留的位置是 \(i\) 时的最大收益,那么转移有 \(f[i]=\max\limits_{j<i,h[j]<h[i]}(f[j]-\sum\limits_{j<k<i}[h[k]>h[i]]c[k]+p[i])\),可以用线段树优化。右侧同理。

提交记录

D1T4

做过类似的题目 Password,做法是大致相同的,就是用图论建模。

考虑对于一种操作 \([l,r]\),连边 \(l\rightarrow r+1\),这样我们可以用最短路求出翻转任意区间的最小代价。

因为只用翻转两个区间,翻转的方式很少,可以简单判断。

提交记录

D2T1

可能算是结论题。

首先考虑如果我们知道了 A 数组,如何判断是否合法。

条件是 \(a_i\le premax_{i-1}+1\)。考虑构造证明。每填一个数时,以这个数结尾的 LIS 的前一个可以是前面的任意一个数,那么 \(a_i\) 应该可以取 \([0,premax_{i-1}+1]\) 中的任意一个数,因为在这样的构造中,前缀最大值每次至多增加 1,那么前缀最大值以内的所有数都存在,就可以都取到。

然后考虑计数。

  1. 如果存在一个位置使得它比前缀最大值大超过 2,那么显然无解。
  2. 如果存在一个位置使得它比前缀最大值大 2,那么只能有一个这样的位置,而且前缀最大值 +1 这个数可以填在最远的前缀最大值和当前数中间的位置。
  3. 否则除开最后一个位置,每个位置有前缀最大值个数可以填(不 +1 是为了防止有重复),最后一个位置可以填前缀最大值 +1 个数,累加起来就是答案。

提交记录

D2T2

挺不错的题目。

容易想到考虑每一段时间的贡献。假设这一段开始是员工 A,结束是员工 B,有 4 种情况:

  1. A 进来,B 出去,这一段时间直接可以关门;
  2. A 进来,B 也进来,那么无论 A 是否有钥匙,只有 B 有钥匙这一段时间才可以关门;
  3. A 出去,B 也出去,和上一种类似,无论 B 是否有钥匙,只有 A 有钥匙这一段时间才可以关门;
  4. A 出去,B 进来,那么只有 A,B 都有钥匙这一段时间才可以开门。

我们观察贡献的形式,发现是形如如果一个人有钥匙那么会有一些时间可以关门,两个人都有钥匙那么有一些时间可以开门,如果转到图上,就等价于每个点有点权,有一些带边权的有向边,我们要给 k$ 个点染色,要使被染色的点的点权和加上两端都被染色的点的边权和最大。

同时我们可以发现更强的性质。对于一个点,出边和入边最多只有一条,而且起始点和终止点的度数都是 1,换言之,这张图的每个联通块都是链,那么就可以直接 DP 了。

具体地,我们设 \(f[i][j][0/1]\) 表示考虑到第 \(i\) 个点,已经给 \(j\) 个点染色,第 \(i\) 个点是否染色的最大贡献,转移时枚举 \(i-1\) 的状态和 \(i\) 的状态进行分类讨论即可。

提交记录

D2T3

树剖板子。离线下来建树,然后在链上用线段树维护即可。

提交记录

D3T1

很有意思的题目。

首先容易做到 \(O(n^3)\)。考虑怎么优化。

我们先考察排序的区间和回文区间的关系。

  1. 如果两个区间无交,那么显然排序不会对回文串长度有影响。
  2. 如果排序区间包含了回文区间,那么答案就是最多的相同字符数,容易求出。
  3. 剩下的情况可以根据两个区间的中点的关系分成两种,这两种可以通过把回文串翻转来转化,于是只用考虑一种。

不妨设排序区间的中点在回文串中点右侧,根据排序区间和回文串右半部分的关系,可以分为 4 种,但这 4 种可以一起考虑。(下面的图都来自 官方题解
img

具体地,对于前两种情况,我们枚举回文串中心,暴力往外拓展找最长的回文串,然后考虑两边的贡献;对于后两种情况,我们枚举排序区间左侧的前一个数作为的左端点,往右找到最近的比左端点前的数小的第一个数,那么最终回文串中心的一段都是这个数,然后再考虑两边的贡献。这时两种情况后面的处理大致相同了。

我们从左端往左找最长的不下降连续段,记录每个数的出现次数,然后从右端点开始往右扫。对于前两种情况,遇到小于左端点的数就停止;对于后两种情况,直到遇到比第一个遇到的比左端点小的数更小就停止。对于前两种情况,在扫描的过程中,两边的贡献就是右边从小到大第一个出现次数超过左边的数之前的数的个数;对于后两种情况,贡献就是比左端点小的数的个数加上右边从小到大第一个出现次数超过左边的数之前的数的个数(不算比左端点小的数)。

说起来比较抽象,看图就好理解了。
img
左侧有一个 \(2\)、两个 \(4\)、三个 \(6\),右边有两个 \(1\)、一个 \(2\)、三个 \(4\),右边的三个 \(4\) 已经比左侧的两个 \(4\) 要多了,那么能做贡献的就只有在中间的两个 \(1\),和两边的一个 \(2\)、两个 \(4\),回文串长度就是 \(3+2+3=8\)

需要注意的是对于第一种情况,排序区间的右端点往右的部分也可能和左边形成回文串,提前预处理出 \(f[i][j]\) 表示从 \(i\) 往左,\(j\) 往右最长的相等串的长度即可。

复杂度 \(O(n^2)\)

提交记录

D3T2

发现了每次会加入的一定是确定的,完全没想到可以直接暴力维护当前栈顶的前 3 个。

具体地,我们维护当前栈的前 3 个和上一个取走的卡牌,记作 \(x,y,z,p\),而要么 \(z=y+1\),要么 \(z=p+1\),我们就需要记录 3 维,在开一维 0/1 表示是哪种情况即可。复杂度应该是 \(O(n^3)\)

提交记录

D3T3

传送门

题意: 通信题。

有一棵 \(n\) 个节点的树(节点从 \(1\) 开始标号),Anna 知道树的形态但不知道 Bruno 的位置,Bruno 既不知道 Anna 的位置也不知道树的形态(两人知道自己所处的位置)。Anna 可以在 Bruno 出发前树的每个节点上标上一个正整数,要保证它们都是 \(0\)\(1\)(若不是但是它们小于等于 \(N\) 会有一些部分分)。Bruno 知道他所在位置上的标号和与他一开始所处的位置直接相连的节点以及 Anna 给它们的标号。Bruno 要返回为了要到达 Anna 的位置并且经过最少的边数,Bruno 下一步应该怎么走。

思路:又是通信题,不过好像比前一道通信题简单不少。

部分分是好想的(以下默认以起点为根)。

对于 subtask1,直接每个点的标号都是其深度即可。

对于 subtask2,每个点的标号都是深度对 3 取模的结果即可。

然后来看正解。我们之前要用不止 0/1 的信息是因为要把一个点的父亲和孩子区分开来,而在只有一个孩子的情况下不能处理。我们仔细读一遍题,注意到题目额外给了 B 每个点的编号,这是部分分不需要的,那我们考虑怎么用这个额外信息。我们可以钦定,如果两点上的标号一样,那么就向号大的一边走,否则根据哪个点是 1 决定是向编号大的一边走还是想编号小的一边走。本来,我们需要父亲节点和儿子节点不同,但是不能处理儿子数是 1 的情况,现在借助编号的大小关系就可以解决了。

提交记录

D4T1

不算很难想。

直接把边从大到小排序后一次加入,暴力维护 \(k\) 个并查集,每次加边时二分出第一个可以加入的并查集即可。

提交记录

D4T2

很有意思的交互题。

容易想到用栈来匹配,但是问题在于有两种括号和我们只能储存 22 位 01。

因为操作次数是 \(O(n^2)\) 的,我们可以稍稍暴力一点。具体地,我们枚举每一位,然后判断这一位能否匹配上,需要我们往后扫,用栈来维护当前匹配了多少,同时把信息传回去。每遇到一个字符,我们根据它是哪种括号、是左括号还是右括号分类讨论,同时维护栈的大小。

那么我们需要记录的信息有:要匹配那一位,当前在哪一位,当前栈的大小,当前正在匹配的括号的类型,前 3 种信息需要记录的数字不超过 100,可以用 7 位存下,最后的括号类型可以用 1 位存下,合起来刚好是 \(3\times 7+1=22\) 位。

提交记录

D4T3

神仙题。

首先,每个防壁的移动是独立的,每次移动是 \(O(1)\) 的,那我们就有了 \(O(nm)\) 的暴力做法。

接着我们先去掉一部分无用的激光。假设有连续 3 次激光的大小关系相同,如 \(p_i\le p_{i+1}\le p_{i+2}\),那么我们在从覆盖 \(p_i\) 移动到覆盖 \(p_{i+2}\) 的过程中一定会经过 \(p_{i+1}\),于是我们可以把 \(p_{i+1}\) 删掉。现在我们的 \(p\) 序列一定是波浪形的。

从上一步受到启发,如果防壁的长度极小,每一次都会移动 \(|p_{i+1}-p_i|\),代价很好计算,进一步的,如果每一步都需要移动,我们是很容易求出总代价的,于是我们就要想办法变成每一步都要移动。

如果有 \(|p_{i+1}-p_i|\ge r-l\),那么我们就一定要移动,否则我们尝试删掉一些点。我们考虑特殊的情况,\(|p_{i+1}-p_i|\) 是全局最小,不妨设 \(p_i\le p_{i+1}\),那么我们从覆盖 \(p_{i-1}\) 到覆盖 \(p_{i+2}\) 的过程中,左端点一定会经过 \(p_i\),那么此时就覆盖了 \(p_{i+1}\),于是我们发现 \(p_i,p_{i+1}\) 没有贡献,可以删去。

注意边界情况。

  1. 如果没有 \(p_{i-1}\)\(p_{i+2}\) 那么不用删掉这两个点;
  2. 如果没有 \(p_{i-1}\),那么就不能删掉 \(p_{i+1}\),因为左端点不一定在 \(p_i\)
  3. 如果没有 \(p_{i+2}\),同上,不能删掉 \(p_i\)

这个过程中我们要至少 3 个点,如果不到 3 个点可以暴力操作。

去掉了 \(|p_{i+1}-p_i|<r-l\) 的情况后,就可以简单的通过 \(\sum\limits_{i=2}^n(|p_{i+1}-p_i|-len)=\sum\limits_{i=2}^n|p_{i+1}-p_i|-(n-1)len\) 来求答案了。

上面的删除是按照 \(|p_{i+1}-p_i|\) 从小到大删除的,又因为防壁是独立的,我们可以按长度从小到大处理,这样我们需要删除的一定不会减少,每个 \(|p_{i+1}-p_i|\) 就只会被删掉一遍。

那么我们可以用可删除堆来取出最小的 \(|p_{i+1}-p_i|\),用链表维护删除。复杂度 \(O(n\log n+m\log m)\)

提交记录

posted @ 2024-02-12 10:21  Xttttr  阅读(8)  评论(0编辑  收藏  举报