「SCOI2011」棘手的操作
题目描述
有 $N$ 个节点,标号从 $1$ 到 $N$,这 $N$ 个节点一开始相互不连通。第 $i$ 个节点的初始权值为 $a_i$,接下来有如下一些操作:
U x y
加一条边,连接第 $x$ 个节点和第 $y$ 个节点。
A1 x v
将第 $x$ 个节点的权值增加 $v$。
A2 x v
将第 $x$ 个节点所在的连通块的所有节点的权值都增加 $v$。
A3 v
将所有节点的权值都增加 $v$。
F1 x
输出第 $x$ 个节点当前的权值。
F2 x
输出第 $x$ 个节点所在的连通块中,权值最大的节点的权值。
F3
输出所有节点中,权值最大的节点的权值。
输入格式
输入的第一行是一个整数 $N$,代表节点个数。
接下来一行输入 $N$ 个整数,$a_1,a_2,\ldots ,a_N$,代表 $N$ 个节点的初始权值。
再下一行输入一个整数 $Q$,代表接下来的操作数。
最后输入 $Q$ 行,每行的格式如题目描述所示。
输出格式
对于操作 F1
,F2
,F3
,输出对应的结果,每个结果占一行。
数据范围与提示
对于 $30\%$ 的数据,保证 $N\le 100,Q\le 10000$。
对于 $80\%$ 的数据,保证 $N\le 100000,Q\le 100000$。
对于 $100\%$ 的数据,保证 $N\le 300000,Q\le 300000$。
对于所有的数据,保证输入合法,并且 $-1000\le v,a_1,a_2,\ldots ,a_N\le 1000$。
看上去还是挺裸的可并堆,除了F3操作,只是要支持单点修改、区间修改、单点查询。
为了区间修改打标记,就不能用一个并查集处理连通问题,只能老老实实记录可并堆里面父亲节点是谁。查询时使用类似LCT中下放操作的方法,用一个栈依次下放从节点到根路径上每一个点即可。
事实上,本题单点修改比区间修改更恶心,因为区间修改不会改变堆的性质,而单点修改就必须把该点从堆里先删除出来,再修改,最后合并回去。如果采用的是左偏树,这个操作很可能会破坏左偏性质,而且由于下放操作的存在,并没有维护左偏性质的必要(效率上说不定更慢)。所以采用斜堆会比较容易。
对于F3操作,发现关注的只是所有堆顶元素的最值。为了快速得到所有堆顶元素的最大值,可以采用两种方式:
1.使用multiset,堆顶元素发生变化时做出相应的删除与添加操作即可。
2.开两个优先队列,采用“垃圾堆”的思路,将需要添加的元素放进一个堆,需要删除的元素放进另一个堆。如果两个堆顶元素相同,pop掉两个堆的堆顶元素即可。
下面的代码中采用的是方式2。
很久之前写的代码了,现在看起来很丑。
1 |
|