寒假第一周解题报告 刘金宗
一: 食物链
题目链接:https://vjudge.net/contest/146798#problem/D
题目大意:有一种食物链A吃B, B吃C, C吃A 。给n个动物, 但是不知道他们是哪个物种, 给出k句话,有两种陈述方式:第一种说法是"1 X Y",表示X和Y是同类。 第二种说法是"2 X Y",表示X吃Y。
其中有一些是假话, 输出假话的个数。满足三个任意一个就表示是假话:
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中X或Y比N大,就是假话;
3) 当前的话表示X吃X,就是假话。
数据范围:1 <= N <= 50,000 0 <= K <= 100,000;
解题思路:这是一道并查集的问题, 可以把三个物种分成三个区间 [1 n], [1 + n, 2 n], [1 + 2n, 3n]这样考虑 如果x y 是同类, 那么 x 和 y就在一个集合内(他们的father是相同的) 如果x吃y 那么 x和 y + n 应该在一个集合内; 同理还有 x + n, y + n, x + 2n, y + 2n 一次类推。 所以每次只需要判断是否在一个集合内, 就可以知道是不是假话, 如果是真话, 则需要把他们再并到相应的集合中。
二:
Frequent values
题目链接:https://vjudge.net/contest/146798#problem/B
题目大意:多组数据, n 和 q 之后一行 给n 个数, 已经是从小到大排好的, 之后有q个询问, 每个询问两个数l, r; 问区间 l r 中出现次数最多的数出现了几次。
数据范围:1 ≤ n, q ≤ 100000 −100000 ≤ ai ≤ 100000
解题思路:这道题需要用到RMQ算法, 再稍微注意点细节。 首先是想到了用一个数组cont【i】表示到当前num[i]出现的最多次数, 之后再用rmq 。
这道题需要注意的是查找的时候, 当左边界正好是两个数的分界的时候,正常查找, 如果不是 那么就会出错, 所以要看左边界这个数有几个, 单独特判扫一下, 左边界移动之后的一个新的范围再查找, 但还需要特别注意, 如果左右边界值相同, 那么左边界移动可能会移动到右边界右边, 那么之后就会re, 所以这个时候直接用r – l + 1 就可以了 。
三: I.O.U.
题目链接:https://vjudge.net/contest/146774#problem/G
题目大意: 给定n个人, 你知道他们之间的债务关系, 问最小的债务和为多少。
数据范围:1 ≤ n ≤ 100; 0 ≤ m ≤ 104
解题思路:这道题看了题解之后很简单, 几句代码就写完了。 思想是看成有一个超级大的银行, 也就是一个数组, 每个人借钱还是外借钱都通过这个银行, 最后要看的就是他们的和/2。 自己想了一下, 这个思路的确没错。 我之前想了建树。。。各种跑偏。
注意 最后求和要取abs
四:
Generating Sets
题目链接:https://vjudge.net/contest/146774#problem/E
题目大意:给定n个数的数列Y,每个数都不同,这是数列是由数列X生成的。X中每个数都不同,并且生成方法有三种
1、保持原来的
2、x替换成x*2
3、x替换成x*2+1
需要求使X中最大数尽量小的数列X。
数据范围:1 ≤ n ≤ 50 000 1 ≤ yi ≤ 109
解题思路: 没做这题之前还不会队列, 思路是找出最大值, 然后/2 看有没有相同的 有的话就继续除, 没有的话就把它放回去 但是没有一个好的办法每次都找出最大值, 如果在循环里用sort 的话复杂度就是O(n^2lgn)而n是5e4, 肯定会T, 于是就上网上搜了题解, 发现应该用队列这个东西,并且可以每次取出的都是最大值, 如果有一个数之后一直除到了0还是放不了 这个数就是最大值了, 其他的数在变小也没有意义了, 就可以直接break了。
队列用priority_queue<int> q;
这样q.pop();得到的就是一个最大值;
这道题yi的数据范围是1 ≤ yi ≤ 109 , 所以要用map来一个vis数组表示集合内是否出现了这个数。