校内题目锦鲤抄

v>

3.锦鲤抄
(zay.cpp/c) 【题目背景】
你在尘世中辗转了千百年
却只让我看你最后一眼
火光描摹容颜燃尽了时间
别留我一人,孑然一身
凋零在梦境里面。
——银临&云の泣《锦鲤抄》
【问题描述】
这首歌的文案讲述了这样一个故事:
在一个兵荒马乱的年代,有一位画师叫浅溪,非常喜欢画锦鲤。战火烧到了泰
安,他的邻居都惊慌逃命,只有他不舍得池中锦鲤没有离开。这天晚上庭院失火,
池中的锦鲤化妖,用生命护住了画师的平安。
注意:由于本题题面涉及到文案故事,在下方提供了便于理解的另一题面版本。
扶苏被画师和锦鲤的故事深深地打动了。为了能让锦鲤和画师继续生活在一起,他
决定回到着火的庭院中灭掉大火。
画师的庭院可以抽象成一个有向图,每个点代表着一个着火的位置。为了量化火势
的大小,扶苏给每个点一个火力值,火力值越大,代表这个点的火势越强。风助火势,
火借风力,对于每一个着火点,都有可能因为大风使得火扩散到其他点。有向图的每条
边 <u,v> 代表大火是从点 u 扩散到点 v 的。需要注意的是一个点可能会扩散到很多
点,也可能是由很多点的大火一起扩散成的。为了不因为灭掉火源让画师发现有人在帮
他灭火,在任意时刻,扶苏不能灭掉任何一个不被任何点所扩散的点的火。一个点的火
被灭掉后,所代表该点的火扩散的所有边将消失。需要说明的是,虽然边消失了,但是
该点扩散到的所有点属性除入度以外都不会改变,更不会消失。
因为穿越的时间有限,扶苏只能灭掉最多 k 个点的火。忙着写题面的扶苏没有时间
算出他最多能扑灭多少火力值,于是他把这个问题交给了你。
便于理解的题面版本:
给你一张有向图,每个点有一个点权。你可以任意选择一个有入度的点,获得它的
点权并把它和它的出边从图上删去。任意时刻不能选择没有入度的点。最多能选择 k 个
点,求最多能获得多少点权。
【输入格式】
输入文件名为 zay.in。
输入文件中有且仅有一组数据,第一行为三个正整数 n,m,k,代表有向图的点数、
边数以及最多选择的点数。
第二行有 n 个整数,第 i 个整数代表节点 i 的火力值(点权)
下面 m 行,每行两个正整数 u,v,代表大火是从 u 扩散到 v 的,即有向图的边。
v>
【输出格式】
输出文件名为 zay.out。
输出一行一个正整数,代表答案。
【输入输出样例 1】
zay.in zay.out
7 7 3 24
10 2 8 4 9 5 7
1 2
1 3
1 4
2 5
3 6
3 7
4 7见选手文件夹下的 Samples/zay/zay1.in 和 Samples/zay/zay1.ans。
【样例1 解释】
样例一如左图。
所选择的点为 3,5,7 三个节点,
所获得的火力值之和为 8+9+7=24.
可以证明这是最优的方案。
【输入输出样例 2】
见选手文件夹下的 Samples/zay/zay2.in 和 Samples/zay/zay2.ans。
【数据规模与约定】
本题共 5 个子任务,捆绑测试,各子任务不等分。各子任务的数据性质如下表
子任务编号 n= m= 特殊性质 子任务分数
1 10 50 特殊性质1 5
2 11 51 无 25
3 100002 500002 特殊性质2 30
4 100003 100003 无 35
5 1000004 5000004 无 5
对于100%的数据,保证0≤给出的点权≤1000,0≤k≤n
特殊性质1:保证给出的点权一定为 0
特殊性质2:保证给出的图是一个有向无环图
v>
【提示】
1、本题输入量极大,请注意大数据的读入对程序效率造成的影响
2、根据 n 和 m 的末位数字可以帮助你快速的判断子任务类型以及特殊性质。
3、本题进行捆绑测试,如果你不知道什么叫捆绑测试,请阅读选手文件夹下的选手
须知。
 

题解:

v>

子任务 1:
点权都是0,于是无论怎么选答案都是 0,输出 0 即可。期望得分 5 分。
子任务 2:
爆搜,枚举所有可能的顺序,然后计算答案。
由于保证了数据随机,可以在搜索的过程中进行剪枝,效率很高,期望得分25
分。
子任务 3:
给出的是一个 DAG 。考虑对于一个 DAG 来说,一个良好的的性质就是在拓扑
序后面的点无论如何变化都无法影响到前面的点。这个题也一样。对于任意一个不
出现原图中本身入度为 0 的点的序列,只要按照拓扑序选点,就一定能取遍序列中
所有的点。
于是发现这张图上入度不为0的点事实上都可以被选择。于是我们把所有入度不
为0的点排一下序,求前k个就可以了。时间复杂度 O(nlogn),期望得分30
子任务 4、5:
考虑DAG的情况放到普通有向图上会发生什么。
有了子任务 3 的提示,我们可以考虑把整个图缩点,将其变成一个DAG来做。
对于一个DAG,显然可以通过子任务 3 调整顺序的方式使得每个强连通分量的
选择情况除选点个数以外互不影响。故下面只讨论一个强连通分量内部的情况。
一个强连通分量显然可以看作是一棵外向树加上很多边得到的。
一棵外向树的定义:一个外向树的任意一个节点要么为叶节点,要么它与孩子
间的所有边都是由它指向孩子。
一棵外向树显然是一个 DAG 。按照之前对 DAG 上情况的说明,显然我们可以
选择除了根节点以外的任意节点。
因为一个强连通分量内部是互相连通的,于是我们不妨钦定一个点为根。
对于一个没有入度的强连通分量,我们不妨钦定点权最小的点为根。这样显然
选择的是最优的。
对于一个有入度的强连通分量,我们不妨钦定那个有入度的点为根。这样在选
择到只剩根节点的时候,因为根节点有入度,所以根节点是可以被选择的。于是这
个强连通分量可以被全部选择。这显然是最优的。
这样综合上述讨论,有入度的强连通分量可以随便选,没有入度的强连通分量
去掉最小的点权的点。剩下贪心取前 k 个就可以了。
进行一次 tarjan的复杂度是 O(n+m),选前 k 个点可以排序一下。这样总复杂
度 O(m+nlogn),期望得分 35 分。注意到复杂度瓶颈在排序上,考虑我们只需要前
k 大而不需要具体前 k 个之间的大小关系,于是使用 std::nth_element()函数可
以将复杂度降至 O(n+m)。期望得分 40 分。注意,输入规模到了 10
7
级别,需要
fread 来实现读入优化。
原文地址:https://www.cnblogs.com/lbssxz/p/11073372.html