依旧是外链
【题目背景】
你在尘世中辗转了千百年
却只让我看你最后一眼
火光描摹容颜燃尽了时间
别留我一人,孑然一身
凋零在梦境里面。
——银临&云の泣《锦鲤抄》
【问题描述】
这首歌的文案讲述了这样一个故事:
在一个兵荒马乱的年代,有一位画师叫浅溪,非常喜欢画锦鲤。战火烧到了泰安,他的邻居都惊慌逃命,只有他不舍得池中锦鲤没有离开。这天晚上庭院失火,池中的锦鲤化妖,用生命护住了画师的平安。
注意:由于本题题面涉及到文案故事,在下方提供了便于理解的另一题面版本。扶苏被画师和锦鲤的故事深深地打动了。为了能让锦鲤和画师继续生活在一起,他决定回到着火的庭院中灭掉大火。
画师的庭院可以抽象成一个有向图,每个点代表着一个着火的位置。为了量化火势的大小,扶苏给每个点一个火力值,火力值越大,代表这个点的火势越强。风助火势,火借风力,对于每一个着火点,都有可能因为大风使得火扩散到其他点。有向图的每条边 <u,v> 代表大火是从点 u 扩散到点 v 的。需要注意的是一个点可能会扩散到很多点,也可能是由很多点的大火一起扩散成的。为了不因为灭掉火源让画师发现有人在帮他灭火,在任意时刻,扶苏不能灭掉任何一个不被任何点所扩散的点的火。一个点的火被灭掉后,所代表该点的火扩散的所有边将消失。需要说明的是,虽然边消失了,但是该点扩散到的所有点属性除入度以外都不会改变,更不会消失。
因为穿越的时间有限,扶苏只能灭掉最多 k 个点的火。忙着写题面的扶苏没有时间算出他最多能扑灭多少火力值,于是他把这个问题交给了你。
便于理解的题面版本:
给你一张有向图,每个点有一个点权。你可以任意选择一个有入度的点,获得它的点权并把它和它的出边从图上删去。任意时刻不能选择没有入度的点。最多能选择 k 个点,求最多能获得多少点权。
【输入格式】
输入文件名为 zay.in。
输入文件中有且仅有一组数据,第一行为三个正整数 n,m,k,代表有向图的点数、边数以及最多选择的点数。
第二行有 n 个整数,第 i 个整数代表节点 i 的火力值(点权)
下面 m 行,每行两个正整数 u,v,代表大火是从 u 扩散到 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、本题进行捆绑测试,如果你不知道什么叫捆绑测试,请阅读选手文件夹下的选手须知。
题解:
子任务 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 分。注意,输入规模到了 10e7级别,需要fread 来实现读入优化。
原文地址:https://www.cnblogs.com/this-is-M/p/11073402.html