SCPC Round #13(Div.2)

第二十届西南科技大学ACM大学生程序设计竞赛题解

(xxx):代表idea贡献者

前言:感谢选手们的参与,对于比赛中的锅,深感歉意。以下是题解部分。

我算算算(charan)

知识点:数学

题意:给定一个数组 ,问多少种不同排列 经过生成方式 可以得到

题解:

的性质,我们可以对数组 中任意相邻元素进行分类讨论。

为方便,我们用 表示: ,

  • ,则在原排列 固定,不对方案数造成影响
  • ,则在原排列 可以选择 之间的数,但是在 之后已经选择 个元素且其中一个元素为 ,所以 仅可选择剩余的。由乘法原理可以将答案与 相乘。
  • ,显然不存在这中情况,方案数则为 0.

还需要注意,特判

图腾(charan)

知识点:线段树,STL,思维

题意:初始给定 个图腾,后有 次询问,每次询问分两种,一种单点插入一个图腾,另一个是区间查询能量,每个整数点计算公式为:

题解:

对于第一个操作:在某个点加入图腾,对于该操作,会发现其实插入一个图腾,影响的仅是原本该位置左边最近和右边最近的两个图腾之间的点。对于寻找最近的点,可以用 来寻找右边最近,用 来寻找左边。然后将当前插入点 的能源先清零,然后用线段树将区间内能源全改为,同理将区间内全改为 ,即可。但是该修改不能将每个点都修改(若全修改复杂度为),此时可以借助带 的线段树来实现。使得该操作复杂度为

对于第二个操作:只需要在线段树中添加一个维护区间和的即可,该复杂度为

祖玛(wzy)

知识点:思维,manacher 或 二分+hash

题意:给定一个字符串 ,问放入一个字符后,通过碰撞消去的小球的最大分数。

题解:

首先会发现对于一个连续的由相同字母组成的子串,该子串在碰撞的时候可以等价于一个该字母,分数为该字母分数×子串长度。

将字符串 每个连续的由相同字母组成的子串缩成一个字母后得到新的字符串 。当进行操作时候,肯定选择为某个回文串,因为对于回文串,当我们发射一个小球于回文串中点,可以使得该回文串全部消失。

所以我们可以用 manacher 算法,去预处理出 所有点的回文半径,放入 ,或用二分+ hash 来代替manacher,对于每一个点去二分回文半径。然后用前缀和去预处理出 的分数,放入 。然后暴力枚举 所有的点,对于点 其最大可以获得分数为 再加上插入的点,和答案取 即可。

朔月望(asoulcoming)

知识点:模拟

题意:给定五个牌,第1、2、4 为齐夏的牌,第1,3,5为嘉心糖的牌。然后遵循游戏规则进行一把游戏。问齐夏是否会获胜?

题解:

按照题意先将牌变换成点数后进行胜利第一条件的判断:对点数进行相加看是否大于 ,然后比较大小,若胜利再判断第二胜利条件,按照牌型优先级依次比较即可,若平局则删去点数后重新比较,直到平均(即齐夏没赢),或分出胜负。

我数数数数数数(charan)

知识点:树形dp

题意:给定一棵顶点数为 的树,问导出子图中任意两个节点间简单路径长度 的方案数为多少。

题解:

由于要求导出子图两节点间简单路径长度。所以对于一个节点,与他相连也至多一个节点。所以对于任意一个节点,有且仅有三种状态:

  1. 不选择当前节点。
  2. 选择当前节点,但是不与子节点相连。
  3. 选择当前节点,与子节点相连

从而可以设 表示第 个节点第 个状态的方案数。

初始化时候,叶子节点的

对于当前节点 ,如何从子树转移?

  1. 不选择当前节点。则可以从子节点的任意状态转移。

  2. 选择当前节点,但是不与子节点相连。则可以从子节点的 状态 1 (不选择子节点)转移。

  3. 选择当前节点,且与子节点相连。则任意选择一个子节点的状态 2 与该节点相连,别的子节点仅可选取状态 1、状态 2、状态 3。

    该公式需要 复杂度,但是我们可以利用 来进行 的转移:

最后答案即为 。注意对答案及时的取模!

异或症(ARUME)

知识点:思维,位运算

题意:给定一个长度为 的排列,可以任意次操作,选择两个数 使,操作后再施行 问最大的

题解:

不妨设 的二进制最高位为 。对于 的二进制位中,从第 位到第 位,若 位为 , 则可以对,经过此次操作后即可使得 。然后对第 个数到第 个数异或自身即可。既可以得到操作后的序列 ,操作后再执行 ,即可得到最终序列 即可得到最大和为:

又双叒叕分糖果(Small Orange)

知识点:思维,STL

题意:两个糖果包有 种不同重量的糖果,但总数相同。现在要把两个糖果包糖果各丢弃一半,并且尽可能多的从两个糖果包中选择糖果做成一个新的糖果包,且新糖果包没有相同重量的糖果。问新糖果包最多可能有多少种糖果?

题解:

首先将第一个糖果包的重量放入 中存储,第二个糖果包的重量放入 存储,同时记录重复重量的糖果

然后可以知道第一个糖果包有第二个糖果包没有的为 ,第二个糖果包有第一个没有的。两边尽可能的取 ,若取不到的时候再拿相同的来补充。即最终答案为:

端午重现。祝全体老师、同学、志愿者们,端午安康!(风前絮)

知识点:计算几何

题意:给定四个平面坐标 ,问 的距离。

题解:可以写一个 函数来计算两点之间的距离,然后调用即可。

double dis(Point a,Point b)
{
	return sqrt( (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) );
}

能源(TANGhaozhan)

知识点:排序

题意:给定 个庇护所,每个至少需要 能源, 个电池,每个电池提供 电源。每个庇护有且仅能装一个大于等于其所需能源的电池。问最后提供能源总和最少多少?

题解:

从贪心的角度来看,提供给庇护所的电池能源要尽可能接近其所需的能源,否者显然不会更优。

所以对 数组排序。然后维护一个指针 ,指向 数组。数组 从小到大遍历,对于每一个 若当前的 ,则就让 提供给 庇护所,把能源加入答案, ;否者 ,直到找到

简单的求和 (sisanjiujiu)

知识点:数学,wqs二分,斜率优化dp

题意:给定,和数组 。定义,将序列 分成 段,求的最小值。

题解:

可以将 简化:

可以预处理前缀和 ,从而得到

对于恰好分成 段问题,若用暴力的方法取处理通常都是 ,但经常都是使用 wqs 二分来处理,可以降一维达到。二分斜率找到与原凸包相切的地方,找到此时的 方程中的最佳段数 与原 进行比较,来更新二分区间。二分斜率通常都是多加一个常数 ,表示多划分一段的增量,原公式可以继续化简为斜率优化dp常见方程式,即 :

此时

套用模板即可解出答案。但要注意特判平台位置!

拾取糖果(charan)

知识点:数论

题意:在 直角坐标平面上有 个糖果,每个糖果用坐标 表示。你可以在 任意一条 穿过原点的直线上拾取糖果,问 最多 能拾取多少个糖果?

题解:

由于经过原点,即 ,即通过记录每个 对应着几个点,去寻找最大值即可。但是由于该 为小数,有精度问题。所以需要使用 去化简分式保存于 中。然后最后用遍历即可。

枝江一梦(asoulcoming)

知识点:预处理

题意:给定二维平面,初始在 正半轴有 点都在 处。有 次操作,每次操作会使得在 点提高 然后扩散其周围,但是 递减,直到 。问最后正半轴 轴坐标。

题解:

对于每次操作,只要维护一个前缀差分,和一个后缀差分,还有对于每个 位置的增量。前后缀维护每个位置应当减少 的量。例如若在 提高 点。则 ,预处理后先从前往后遍历,处理右边的扩散,再从后往前遍历,处理左边的扩展。代码如下:

	int add = 0;
	int sum = 0;
	for(int i=1;i<=n;i++)
	{
		sum += w[i] - add;
		add += pre[i];
		ans[i] = sum;
	}	
	add = 0;
	sum = 0;
	for(int i=n;i>=1;i--)
	{
		sum += w[i] - add;
		add += pos[i];
		ans[i] += sum - w[i];//注意此时w[i]加了两次,所以应当减去一次。
	}

最后输出ans数组即可。

简单动态规划(Small Orange)

知识点:思维,预处理,扫描线,前后缀和

题意:给定一个长度为 的序列 ,可以做任意次操作进行修改序列,但操作花费不同,问操作后使序列满足公差为 的等差数列的最小花费。

题解:

首先可以对原序列排序,因为交换元素不需要花费。排序后的序列必然是最后的顺序,可以比较简单证明:

若排序后的序列顺序不是最后顺序,则存在两个下标 ,在排序后,但是,很明显,两个之间必然有交集,即为重复部分,该部分肯定会使得答案不是最优。证毕。

既然为最后序列,我们可以对该序列减去公差,,即可消去等差数列的性质,得到的序列,我们只需要他相同即可。

对于最终答案,其某个位置必然为 ,可以简单证明:

若最终答案不为该三个位置。则有两种情况

  • 可能在任意两个数(或三个及以上数)之间。
  • 若两个数之间,则和在该数一样的操作次数,即花费不变。
  • 若三个及以上之间,两边不均分的情况下,显然在中间会使得一边数多的操作次数增大,即更不优。若均分,则和两数之间相同,即操作次数不变。
  • 可能在左端点左边或右端点右边
  • 若在两端,显然步数要多走 的倍数,显然不优 。

(因为三个位置中必然有奇数和偶数,即可以不用讨论奇偶的影响,讨论中的操作仅为 的操作)

所以我们可以把所有可能的位置放入 中排序后去重,然后判断取哪一个点的可以使得花费最小。

如何判断选取某一点的花费?

可以前后缀预处理处值的总和,奇数个数总和,偶数个数总和,然后对于某个可能的取值 找出其在中的位置,对其前面的不同奇偶性的数 ,对后面不同奇偶性的,然后差值总和除 ,乘上操作花费即为该点的花费,取min 即可取得最小花费。