[CF878C] Tournament 题解
这个题也是非常神奇了。
CF 评分 2700,看起来很难搞的样子,其实难度并没有想象中那么大,主要是要利用 C++ 的优势——STL。
废话不多说,直接讲做法,考虑我们先定义一个说法,如果两个人 \(A\) 和 \(B\) 满足 \(A\) 任何运动都强于 \(B\),则称 \(A\) 吊打 \(B\)。
想想什么时候只有一个人能夺冠,那就是他吊打所有人的时候,而如果一个人可能夺冠,条件不需要这么苛刻,只要他要能间接打败所有人,比如石头剪刀布,三个对决的时候次序决定了冠军。
考虑加入我们已经维护好一个冠军集合,即这个集合里的人都可能成为冠军,那么一个人如果想成为冠军,那么他只需要将这个集合中的某人从某个方面打败就好了,这样的话先执行之前这个人获得冠军的对局,然后再让新人打败他就好了,但如果他想要让别人无法成为冠军,就必须吊打这个集合里的所有人。
同时需要注意如果新人能成为冠军,而之前本来无法成为冠军的人可以打败这个新人,那么这个人也可以成为冠军,同理能打败这个人的人也能成为冠军。
维护起来其实不麻烦,我们考虑模仿冠军集合维护很多集合,每个集合表示一群人,这群人单独取出来每个人都能成为冠军,接下来就是新人加入时对这么多集合进行维护。
先要减少集合维护的信息,我们发现我们只要记录每个集合每个运动最强者和最弱者的能力值就好了,用来判断能不能打败和被打败,然后再记录人数就好了,同样,如果一个集合 \(A\) 每个运动的最强值都弱于另一个集合 \(B\) 该运动的最小值,我们称集合 \(A\) 吊打 \(B\),如果 \(A\) 和 \(B\) 都不会互相吊打,那么这两个集合显然是可以合并的,而冠军集合就是能吊打其他所有集合的那个集合。
接下来就是要维护这么多集合的关系,发现 C++ 的 STL 里有个东西叫做 set
,我们可以方便地使用这个东西,首先定义比较运算符,出于形象我们定义大于号,我们用吊打定义大于号,这样的话 \(set\) 内置也会把等号定义好,就是满足 \(A\not>B\land B\not>A\),这样的话自然地就形成了两个集合可以合并的条件,所以我们可以将新插入的人一个人看做一个集合,每次用 set
的 find
函数找出 set
中与新人可以合并的集合,就可以直接维护合并,然后取出最大的集合输出大小就好了。
代码实现上难度不大,因为 STL 给我们带来了很大的便利。