LuoguP3377 【模板】左偏树(可并堆)
题意
如题,一开始有
-
1 x y
:将第 个数和第 个数所在的小根堆合并(若第 或第 个数已经被删除或第 和第 个数在用一个堆内,则无视此操作)。 -
2 x
:输出第 个数所在的堆最小数,并将这个最小数删除(若有多个最小数,优先删除先输入的;若第 个数已经被删除,则输出 并无视删除操作)。
对于 int
范围内。
思路
【模板】左偏树?【模板】启发式合并!
左偏树这种不常用的 useless algo 想来想去也没啥实战意义,真要说的话也完全可以用动态开点权值线段树的线段树合并来上位替代。
于是我们就有了充分的理由用邪道来艹题。
由于只有合并莫得分裂,考虑启发式合并。每次将较小的堆向较大的堆合并即可做到均摊复杂度
对于删除直接弹堆顶然后打标记即可。
代码
“ 主播,set
是你爹?用小根堆压掉取堆顶的一个
const ll maxn=1e5+5;
ll fa[maxn];
bool del[maxn];
ll find(ll x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
void merge(ll x,ll y){
ll u=find(x),v=find(y);
fa[u]=v;
}
struct node{
ll val,id;
friend bool operator < (const node &x,const node &y){
return x.val==y.val?x.id<y.id:x.val<y.val;
}
};
set<node>s[maxn];
ll n,m;
void solve(){
n=R,m=R;
for(ll i=1;i<=n;i++){
fa[i]=i;
ll x=R;
s[i].insert((node){x,i});
}
while(m--){
ll opt=R,x=R;
if(opt==1){
ll y=R;
if(del[x]||del[y]||find(x)==find(y)){
continue;
}
ll u=find(x),v=find(y);
if(s[u].size()<s[v].size())swap(u,v);
while(!s[v].empty()){
auto it=s[v].begin();
s[u].insert(*it);
s[v].erase(*it);
}
merge(v,u);
}
else {
if(del[x]){
puts("-1");
continue;
}
ll u=find(x);
auto it=s[u].begin();
//if(s[u].empty())cerr<<"zmhsn?"<<endl;
we(it->val);
del[it->id]=1;
s[u].erase(*it);
}
}
return ;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!