P1456 Monkey King 左偏树模板题

题目链接

https://www.luogu.com.cn/problem/P1456

题意

n只猴子有自己的力量值s,对于每个操作 x,y,在x和y猴子的猴群中分别选出力量最大的猴子,将力量值/2,之后将两个猴群合并。如果本来就在一个猴群,输出-1.

思路

类似并查集的联通属性和合并操作+维护块内最值以及修改=可并堆

思路很清晰,对于两个堆,弹出堆顶,处理值,重新合并回去,再合并两个堆就可以了。

每次merge用一些中间变量记录堆顶就可以了,但板子里的pop函数是没有返回堆顶的,这个要改一下。

代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=200005;
	const int inf=0x3f3f3f3f;
    const int mod=1e9+7;
	int n,m,k;
	struct custom_hash {static uint64_t splitmix64(uint64_t x) {x += 0x9e3779b97f4a7c15;x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;x = (x ^ (x >> 27)) * 0x94d049bb133111eb;return x ^ (x >> 31);}size_t operator()(uint64_t x) const {static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count();return splitmix64(x + FIXED_RANDOM);}};
	struct left_tree{
        int dis,val;
        int fa,lson,rson;
    }lt[maxn];
    void init(){
        memset(lt,0,sizeof lt);
        lt[0].dis=-1;
        for(int i=0;i<maxn;i++)
            lt[i].fa=i;
        
    }
    inline int find(int x){
        return lt[x].fa==x?x:lt[x].fa=find(lt[x].fa);
    }
    int merge(int x,int y){
        if(!x||!y)  return x+y;
        if(lt[x].val>lt[y].val)   swap(x,y);//保证x<y
        int ls=lt[x].lson,rs=lt[x].rson;
        rs=merge(rs,y);
        if(lt[ls].dis<lt[rs].dis)   swap(ls,rs);
        lt[ls].fa=lt[rs].fa=lt[x].fa=x;
        lt[x].dis=lt[rs].dis+1;
        lt[x].lson=ls,lt[x].rson=rs;
        return x;
    }
    signed main(){
		IOS
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
		int tn=1;
		while(cin>>n){
            init();
            for(int i=1;i<=n;i++){
                cin>>lt[i].val;
                lt[i].val*=-1;
            }
            
            cin>>m;
            while(m--){
                int a,b;
                cin>>a>>b;
                a=find(a),b=find(b);
                if(a==b)    cout<<-1<<endl;
                else{
                    int newl,newr,t;
                    lt[a].val/=2;
                    lt[lt[a].lson].fa=lt[lt[a].rson].fa=t=merge(lt[a].lson,lt[a].rson);
                    lt[a].dis=lt[a].lson=lt[a].rson=0;
                    lt[a].fa=lt[t].fa=newl=merge(a,t);
                    lt[b].val/=2;
                    lt[lt[b].lson].fa=lt[lt[b].rson].fa=t=merge(lt[b].lson,lt[b].rson);
                    lt[b].dis=lt[b].lson=lt[b].rson=0;
                    lt[b].fa=lt[t].fa=newr=merge(b,t);
                    lt[newl].fa=lt[newr].fa=t=merge(newl,newr);
                    cout<<-lt[t].val<<endl;
                }
            }
		}
	} 
						

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值