说在前面的话
不要被可怕的操作吓跑,战胜恐惧的最好办法,就是面对恐惧,加油,奥利给。但冷静分析一下,好像是道大水题。
分析
分析一下,所有操作其实有用的就是维护一下,集合最大。支持合并。其他的操作都可以开一些标记记录一下。但是合并操作的复杂度如何分析,最值又如何维护。
维护最值
一般维护最值一般考虑 和平衡树。但是只有查询最值和删除集合中一个数的操作。用平衡树就大材小用了,反而会增加常数。关于如何删除集合中一个树的操作,完全可以使用维护两个堆 ,一个添加堆,一个删除堆。那么当查询最值时,如果两个堆顶一样,便都弹出。最后直接返回第一个堆 的堆顶。这个也是一个非常常用的技巧吧,大概是个延迟删除的操作。
合并
考虑 的启发式合并。我们可以类比 的合并。我们合并两个 或者是堆时。我们是考虑将小的集合插入大的集合中的。我们来分析一下时间复杂度。考虑一个数合并的次数。一开始我们有 个大小为 的集合。经过一次合并,较小的集合的大小就多了一倍。那么一个数总共需要 次合并。那么总的合并复杂度为 由于是用 维护的,所以合并所有集合的总复杂度为 。
细节1:要注意,任何时候变动一个数值的大小,一定要改变当前集合和全局最大值的最值。
细节2:自己打的标记,在输出时一定要加上去。
代码
#include<bits/stdc++.h> using namespace std; #define ll long long #define pb(x) push_back(x) const int N = 610000; int read() { int x = 0,f = 0;char ch = getchar(); while(!isdigit(ch)) {if(ch=='-')f=1;ch=getchar();} while(isdigit(ch)) {x = x * 10 + ch - '0';ch = getchar();} return f ? -x : x; } struct Heap { priority_queue<int> del,add; void ph(int x) {add.push(x);} void pop(int x) {del.push(x);} int top() { while(del.size() && del.top() == add.top()) del.pop(),add.pop(); return add.top(); } }All,s[N]; vector<int> S[N]; int Alltag,tag[N],fa[N],a[N],n; int findset(int x) {return fa[x] == x ? x : fa[x] = findset(fa[x]);} int main() { n = read(); for(int i = 1;i <= n;i++) { a[i] = read(); fa[i] = i;S[i].pb(i); s[i].ph(a[i]);All.ph(a[i]); } int Q = read(); while(Q--) { char ch[10]; scanf("%s",ch + 1); if(ch[1] == 'U') { int x = read(),y = read(); x = findset(x);y = findset(y); if(x == y) continue; All.pop(s[x].top() + tag[x]); All.pop(s[y].top() + tag[y]); if(S[x].size() < S[y].size()) swap(x,y); for(auto to : S[y]) { int fto = findset(to); fa[fto] = x; a[to] = a[to] + tag[y] - tag[x]; S[x].pb(to);s[x].ph(a[to]); } All.ph(s[x].top() + tag[x]); } if(ch[1] == 'A' && ch[2] == '1') { int x = read(),val = read(); int fx = findset(x); All.pop(s[fx].top() + tag[fx]); s[fx].pop(a[x]);s[fx].ph(a[x] + val);a[x] += val; All.ph(s[fx].top() + tag[fx]); } if(ch[1] == 'A' && ch[2] == '2') { int x = read(),val = read(); int fx = findset(x); All.pop(s[fx].top() + tag[fx]); tag[fx] += val; All.ph(s[fx].top() + tag[fx]); } if(ch[1] == 'A' && ch[2] == '3') Alltag += read(); if(ch[1] == 'F' && ch[2] == '1') { int x = read();int fx = findset(x); printf("%d\n",a[x] + tag[fx] + Alltag); } if(ch[1] == 'F' && ch[2] == '2') { int x = read();x = findset(x); printf("%d\n",s[x].top() + tag[x] + Alltag); } if(ch[1] == 'F' && ch[2] == '3') printf("%d\n",All.top() + Alltag); } return 0; }