https://www.luogu.com.cn/problem/P3377
题目描述
如题,一开始有N个小根堆,每个堆包含且仅包含一个数。接下来需要支持两种操作:
操作1: 1 x y 将第x个数和第y个数所在的小根堆合并(若第x或第y个数已经被删除或第x和第y个数在用一个堆内,则无视此操作)
操作2: 2 x 输出第x个数所在的堆最小数,并将其删除(若第x个数已经被删除,则输出-1并无视删除操作)
输入格式
第一行包含两个正整数N、M,分别表示一开始小根堆的个数和接下来操作的个数。
第二行包含N个正整数,其中第i个正整数表示第i个小根堆初始时包含且仅包含的数。
接下来M行每行2个或3个正整数,表示一条操作,格式如下:
操作1 : 1 x y
操作2 : 2 x
输出格式
输出包含若干行整数,分别依次对应每一个操作2所得的结果。
输入输出样例
输入 #1复制
5 5
1 5 4 2 3
1 1 5
1 2 5
2 2
1 4 2
2 2
输出 #1复制
1
2
说明/提示
当堆里有多个最小值时,优先删除原序列的靠前的,否则会影响后续操作1导致WA。
时空限制:1000ms,128M
数据规模:
对于30%的数据:N<=10,M<=10
对于70%的数据:N<=1000,M<=1000
对于100%的数据:N<=100000,M<=100000
思路:参见https://www.cnblogs.com/pks-t/p/10326682.html
#include<bits/stdc++.h>
#define ls tree[x].son[0]
#define rs tree[x].son[1]
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
struct Tree
{
int dis,val,son[2],rt;
}tree[maxn];
int n,m;
int Merge(int x,int y)
{
if(!x||!y)
return x+y;
if(tree[x].val>tree[y].val||(tree[x].val==tree[y].val&&x>y))
swap(x,y);
rs=Merge(rs,y);
if(tree[rs].dis>tree[ls].val)
swap(ls,rs);
tree[ls].rt=tree[rs].rt=tree[x].rt=x;
tree[x].dis=tree[rs].dis+1;
return x;
}
int father(int x)
{
return tree[x].rt==x?x:tree[x].rt=father(tree[x].rt);
}
void Delete(int x)
{
tree[x].val=-1;
tree[ls].rt=ls;
tree[rs].rt=rs;
tree[x].rt=Merge(ls,rs);
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
tree[i].rt=i;
scanf("%d",&tree[i].val);
}
int op,u,v;
for(int i=1;i<=m;i++)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d %d",&u,&v);
if(tree[u].val==-1||tree[v].val==-1)
continue;
int fu=father(u),fv=father(v);
if(fu==fv)
continue;
tree[fu].rt=tree[fv].rt=Merge(fu,fv);
}
else
{
scanf("%d",&u);
if(tree[u].val==-1)
printf("-1\n");
else
{
int fu=father(u);
printf("%d\n",tree[fu].val);
Delete(fu);
}
}
}
return 0;
}