链接
题解
维护一个并查集,每次一个人上线,我就暴力把这个人所在的集合根其相邻且在线的人的集合给合并
一个人下线,就直接把他所在的并查集的 s i z e − − size-- size−−
这样不好的一点在于,每次的代价都是 O ( d e g r e e ( x ) ) O(degree(x)) O(degree(x))的,如果他总是操作一个度数很大的点,就肯定会 T T T
由于所有的点的度数之和等于 2 m 2m 2m,所以度数大于 2 m \sqrt {2m} 2m的点的个数不会超过 2 m \sqrt {2m} 2m个
度数小的我仍然可以使用暴力做法
但是度数大的,我考虑下能不能优化
注意到我们加入一个点的时候:我要把在图上和这个点相邻的而且在线的人都和自己合并
突破点就在这个"在线"
因为每次操作只能造成一个点从不在线变成在线,这就给人一种势能分析的感觉,有些需要势能分析的题目,我们总是在一些操作中积累势能,在某些操作中释放势能。现在我要处理这些度数大的点,就需要找一种“释放势能”的操作。
显然,我要对每个度数大的点记录所有影响到他的修改。也就是,和他在图上相邻的点,如果被激活了,就会影响到他,我们记录这个影响,说的明白一点,对每个度数大的点记录被激活的邻点。
当这个度数大的点一直在沉睡的时候,我就记录那些上线的临点。在度数大的点突然醒来的那一刻,我就要考虑处理这些信息了。显然我只要挨个进行并查集合并就行了,然后把这些信息丢掉(必须丢掉才能释放势能)。
具体一点:当小点上线时,我就暴力处理。同时看一下他是否影响到了某些沉睡中的大点,如果影响了就记录一下,小点上线的复杂度是 O ( d e g r e e ( u ) ) = O ( m ) O(degree(u)) = O( \sqrt {m} ) O(degree(u))=O(m)的。大点苏醒时,先处理前面积累的信息(势能分析得到这一部分是 O ( n ) O(n) O(