[SCOI2011]棘手的操作
[SCOI2011]棘手的操作
https://ac.nowcoder.com/acm/problem/20282
前言
- 不得不说,这题得劲。因为这几天复习数据结构都疯了QwQ,一来就想码并查集+线段树,但是想了一想(
wtcl),似乎不太容易维护每个块。emmm,然后去网上看看题解,发现竟然可以用堆合并与删除解决,以简单方法吊打难题
分析
- 纵观全局,可以先确定能用并查集。每一个都需要维护。而需要维护的则是每一个块的最大值,每一个块有哪些节点
- 维护方法:因为既要求所有节点中的最大值,也要求各个块中的,那就对全局以及每一个块都建立一个堆,同时维护一个容器 c [ i ] 表示在块 i 中的有哪些节点。因为每次对一个点进行修改时,不能直接提出来进行修改(即先进行pop找到节点,修改完后在push回去),那么换种方式,对每一个块再建立一个队列del存下本应该删去的值,每一次取出块中的最大值的时候,先与队列del中的数进行比较
- 详细过程:
U x v:合并x,v所在的两个堆。为了减少时间复杂度,选择把块中节点较少的合并到块中节点较多的。不断取出其中一个堆中的元素,加入到另一个中去
A1 x v:先把它push进入堆del中,修改完再入队
A2 x v:只需要对每一个联通块维护一个变量 jub [ i ] ,与线段树中的懒标记类似
A3 x v:同上,只需要用一个全局变量记录一下
F1 x:输出 a [ i ] + jub [ i对应块的编号 ],就像一个下传操作
F2 x:每一个连通块都建立了一个堆,输出堆顶
F3 x:在全局也建立了一个堆,输出堆顶
代码
/* U x y:合并他们的两个堆 A1 x v:先pop到del,再push回val A2 x v:连通块的变量处理 A3 x v:全局变量处理 F1 x:找到所属连通块,加上局部和全局变量 F2 x:直接pop F3 x:整体的pop */ #include<bits/stdc++.h> #define ll long long using namespace std; const int N=3e5+10; int n,zt,m; ll a[N],jub[N];int f[N]; vector<int>num[N]; //连通块里的点 struct nkl { priority_queue<ll>q,del; void push(ll x){q.push(x);} void pop(ll x){del.push(x);} int top() { while(del.size()&&del.top()==q.top()) del.pop(),q.pop(); return q.top(); } }wl,bl[N]; inline int find(int x) { if(x==f[x]) return x; return f[x]=find(f[x]); } int main() { scanf("%d",&n); for (int i=1;i<=n;i++) { scanf("%lld",&a[i]); f[i]=i;//并查集初始化 num[i].push_back(i);//连通块的点 wl.push(a[i]); bl[i].push(a[i]); //全局与局部的点 } scanf("%d",&m);char ins[5];int x,v; while(m--) { scanf("%s",ins); if(ins[0]=='U') { scanf("%d%d",&x,&v); x=find(x),v=find(v); if(x==v) continue; if(num[x].size()<num[v].size()) swap(x,v); wl.pop(bl[v].top()+jub[v]); wl.pop(bl[x].top()+jub[x]); int len=num[v].size(); for (int i=0;i<len;i++) { int j=num[v][i]; f[j]=x; a[j]=a[j]+jub[v]-jub[x]; bl[x].push(a[j]); num[x].push_back(j); }//开始合并 num[v].clear(); wl.push(bl[x].top()+jub[x]); //重回最大值 } else if(ins[0]=='A') { if(ins[1]=='1') { scanf("%d%d",&x,&v); int u=find(x); wl.pop(bl[u].top()+jub[u]); bl[u].pop(a[x]); a[x]+=v; bl[u].push(a[x]); wl.push(bl[u].top()+jub[u]); } else if(ins[1]=='2') { scanf("%d%d",&x,&v);int u=find(x); wl.pop(bl[u].top()+jub[u]); jub[u]+=v; wl.push(bl[u].top()+jub[u]); } else scanf("%d",&v),zt+=v; } else { if(ins[1]=='3') printf("%lld\n",wl.top()+(ll)zt); else { scanf("%d",&x); int u=find(x); if(ins[1]=='1') printf("%lld\n",(ll)a[x]+zt+jub[u]); else printf("%lld\n",(ll)bl[u].top()+zt+jub[u]); } } } return 0; }
每日一题 文章被收录于专栏
每天的题,为了牛币