比赛连接:Dashboard - CodeTON Round 4 (Div. 1 + Div. 2, Rated, Prizes!) - Codeforces
A. Beautiful Sequence
题意:
t(1≤t≤500)组测试每组给定大小为n(1≤n≤100) 的序列,判断它是否存在一个子序列是好序列。一个序列是好序列当且仅当至少存在一个ai=i。
思路:
考虑到从原序列抽取一些数形成的子序列下标i只可能比原来小,如果原来存在一个i>=a[i],存在一种选择方法使得子序列下标i减小到a[i],否则不可能构造出来。
代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
B. Candies
题意:
t(1≤t≤1e4)组测试每组给定n(2≤n≤1e9) ,有两种操作,第一种是将x变为2*x-1,第二种是将x变为2*x+1,输出将1变为n的操作序列,最多不超过40次。
思路:
操作后的数只能变为奇数,故n为偶数时一定不能构造。n为奇数,考虑如何使n变为1,如果把n写为二进制形式,第一种逆操作相当于将n加一再右移一位,第二种为将n减一再右边移动一位,所以可以找到一种操作:每一次看n的最后两位,如果是11做第二种操作,这样就除去了最后一位,如果是01做第一种操作(不能做第二种操作,因为过程中不能出现偶数)。n的最大是1e9,最多30位,一定符合要求。
代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
C. Make It Permutation
题意:
给定一个序列,可以进行两种操作,删除一个元素,花费为c,添加一个元素,花费为d,求使序列变为一个非空排列的最小花费。
思路:
因为排列不会存在重复元素,遇到重复元素一定要删除。将处理后的数组排序,考虑构造出1~a[i]的排列,删除后面的元素的花费,所有情况取最小值。注意将数组删除为空再补1的情况也要算。
代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
D. Climbing the Tree
题意:
一个蜗牛爬树,树的高度为h,蜗牛每天爬a,下降b(a>b),从高度0花费n天爬上高度h。现在不知道h的值,有两种事件,事件1是给定a,b,n,需判断当前事件是否和前面的冲突;时间2是给定a,b,判断是否能根据当前信息求出确切的n。
思路:
先寻找a,b,h,n的关系,因为最后一次距离顶端小于等于a可以直接上去,考虑爬高度h-a的情况,显然需要
天,最后再花一天爬上h,总天数为
对于事件1,有:

于是可以根据a,b,n的值算出h的下界和上界,分别用lh和rh表示:


将lh和rh和前面的取交集,如果为空说明不合法,否则合法。
对于事件2,根据当前lh和rh可以根据上式计算出n,n唯一则说明有解,否则无解。
注意h<=a,n=1的边界情况。
代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
E. Monsters
题意:
给定一个n个点,m条边的无向无环图,每个点都有一个怪物对应一个危险值,能打败某个点的怪物当且仅当打败的怪物数大于等于危险值。开始选择一个点出发,可以通过一个点需要能打败该点的怪物,判断能否打败所有怪物。
n, m (1≤n,m≤2e5)
思路:
本题官方给出了最暴力的做法:找到每一个ai=0的点做扩散,每一次扩散判断是否能扩展到所有点。虽然直觉上会超时,但是可以证明这样做复杂度为O(n*log(n)*log(n))。
相比官方做法更容易想到用并查集维护连续可走的块。先将危险值从小到大排序,需要一个数组来记录一个集合是否被选择(是否走过),每一次扩散都将危险值大的点并到危险值小的点,维护集合大小,最后合法的结果是一定只有一个根节点。
代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
E题补充说明:
几个数组代表的意义:
vis[i]: i在前面有没有走过,这个作用是保证在无向图中从危险值小的到危险值大的走。
ok[i]: i的块是否合法。
s[i]: 并查集i的根
sz[i]:i的块走过的个数,用于和危险值比较判断是否合法。
首先出发点一定是危险值尽量越小越好,我们考虑从危险值小的点走向危险值大的点,将所有点按照危险值小的到危险值大的排序。然后枚举,比如我们枚举到了u,然后遍历它的邻接点v,需要保证v是否前面遍历过(v的危险值小于等于u的危险值),第69行代码就是判断u这个点是否满足题目条件,最后将v并到u。
私信的一些问题:
Q1:为什么合并的时候不需要判断?
A1:因为这里用并查集只是为了维护一个点可以走到的点数,判断合法用ok数组另加判断。
Q2:并查集根节点代表什么?
A2:根节点为一个连通块危险值最大的点,它的ok的值代表了这个块是否合法。
Q3:为什么危险值为0的时候将ok的值置为1,危险值为1的时候为什么不可以。
A3:题目要求是当前打败的怪物数量大于等于当前点危险度,所以开始打败数量为0,当然危险值也是0。后面的判断也是当前走了的点大于等于危险值。