左偏树(p1456) 比较模板的一道题

题意:有n只猴子,m个操作,每一个操作,会让这两堆猴子里的最大的两只打架,打完之后,自身权值减半,然后他们会成为朋友

也就是会属于同一棵树,细节:如果选出的猴子在同一堆,就输出-1,然后下一个操作,不用打架;

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<string.h>
 4 #include<math.h>
 5 using namespace std;
 6 const int maxn=1e5+10;
 7 int val[maxn];
 8 int f[maxn];
 9 int ch[maxn][2];
10 int dis[maxn];
11 int getf(int x)  //标准并查集
12 {
13     if(f[x]==x) return x;
14     else{
15         f[x]=getf(f[x]);
16         return f[x];
17     }
18 }
19 int Merge(int x,int y)
20 {
21     if(!x||!y) return x+y;  //到底了;
22     //保证最小堆性质     后面这个不懂
23     if(val[x]<val[y]) swap(x,y);
24     //这个大概就是创这个算法的人的习惯了,将其定在右子树。
25     //然后再在下面进行操作来满足偏左树的性质;
26     ch[x][1]=Merge(ch[x][1],y);
27     f[ch[x][1]]=x;   //并查集操作;
28     //满足偏左;
29     if(dis[ch[x][0]]<dis[ch[x][1]]) swap(ch[x][0],ch[x][1]);
30     //这个是偏左树的性质,想想就知道是对的。
31     dis[x]=dis[ch[x][1]]+1;
32     return x;
33 }
34 int main()
35 {
36     int n,m;
37     while(scanf("%d",&n)!=EOF){
38         memset(dis,0,sizeof(dis));
39         memset(ch,0,sizeof(ch));
40         for(int i=1;i<=n;i++){
41             scanf("%d",&val[i]);
42             f[i]=i;
43         }
44         scanf("%d",&m);
45         while(m--){
46             int t1,t2;
47             scanf("%d%d",&t1,&t2);
48             int u=getf(t1);
49             int v=getf(t2);
50             if(u==v){
51                 printf("-1\n");
52                 continue;
53             }
54             val[u]/=2;
55             int root=Merge(ch[u][1],ch[u][0]);
56             ch[u][1]=ch[u][0]=dis[u]=0;
57             int newx=Merge(root,u);
58             val[v]/=2;
59             root=Merge(ch[v][1],ch[v][0]);
60             ch[v][1]=ch[v][0]=dis[v]=0;
61             int newy=Merge(root,v);
62             root=Merge(newx,newy);
63             f[newx]=f[newy]=root;   //这里两个点都要,其实只有一个点需要,
64             //因为只有一个点的父节点没有从已经被减半的根节点那更新回来。
65             //需要更新的点是根节点;
66             printf("%d\n",val[root]);
67         }
68     }
69     return 0;
70 }

原文地址:https://www.cnblogs.com/pangbi/p/11729794.html

时间: 2024-11-10 15:46:11

左偏树(p1456) 比较模板的一道题的相关文章

左偏树(p4431)

难得不是左偏树,而是思维: 这道题在做得时候,有两个性质 1.如果a是一个不下降序列,那么b[i]==a[i]时取得最优解. 2.如果a是一个严格递减序列,则取a序列的中位数x,令b[1]=b[2]=b[3]=...=b[n]=x,即是最优解. 于是在做得时候,我们会分为几个区间,通过区间得合并去做这一道题: 我们根据这两个性质,求出这些区间的最优质,去合并: 三.考虑一般情况 a序列一定不可能这么良心是上面的两种情况. 但它一定是由这两种情况组成的,也就是把a序列看成一段一段的,每一段要么不下

luogu 【P3377】 【模板】左偏树

左偏树模板... #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <algorithm> #include <cctype> #include <iostream> #define For(i, l, r) for(int i = (l); i <= (int)(r); ++i) #define For

【模板】左偏树

左偏树是可合并堆的一种实现方式,可合并堆还有其他实现方式比如斜堆,然而我这种蒟蒻只会写左偏树. 模板里的左偏树为大根堆,支持合并,查询堆顶和弹出堆顶操作,对于已经删除的位置,查询将返回-1,为了确保弹出的正常进行,模板里使用的并查集没有使用路径压缩,因此常数可能会比较大. 1 #include<stdio.h> 2 #define maxn 1000 3 struct node{int ch[2],w,dist;}; 4 int n,op,ori[maxn]; 5 void swp(int &

模板:左偏树

如果你知道priority_queue的话,那自然就知道左偏树的目的了. 左偏树的目的和优先队列一致,就是求出当前所在堆中的最大(小)值. 但是我们作为高贵的C++选手,我们为什么还要学习左偏树呢. 当然是因为priority_queue太!慢!了! ———————————————————————————————————— 概念引入: 对于左偏树,我们引入两个概念: 外节点:如果该节点的左子树或右子树为空,那么该节点为外节点. 距离(dis):该节点到达最近的外节点经过的边的个数. 我们同时将优

P3377 【模板】左偏树(可并堆)

P3377 [模板]左偏树(可并堆) 题目描述 如题,一开始有N个小根堆,每个堆包含且仅包含一个数.接下来需要支持两种操作: 操作1: 1 x y 将第x个数和第y个数所在的小根堆合并(若第x或第y个数已经被删除或第x和第y个数在用一个堆内,则无视此操作) 操作2: 2 x 输出第x个数所在的堆最小数,并将其删除(若第x个数已经被删除,则输出-1并无视删除操作) 输入输出格式 输入格式: 第一行包含两个正整数N.M,分别表示一开始小根堆的个数和接下来操作的个数. 第二行包含N个正整数,其中第i个

【模板】左偏树(可并堆)

题目描述 如题,一开始有N个小根堆,每个堆包含且仅包含一个数.接下来需要支持两种操作: 操作1: 1 x y 将第x个数和第y个数所在的小根堆合并(若第x或第y个数已经被删除或第x和第y个数在用一个堆内,则无视此操作) 操作2: 2 x 输出第x个数所在的堆最小数,并将其删除(若第x个数已经被删除,则输出-1并无视删除操作) 输入输出格式 输入格式: 第一行包含两个正整数N.M,分别表示一开始小根堆的个数和接下来操作的个数. 第二行包含N个正整数,其中第i个正整数表示第i个小根堆初始时包含且仅包

【模板】左偏树(可并堆) 可并堆_并查集

左偏树的树高是 $log(n)$ 级别的,所以在查询祖先的时候是不可以直接顺着左偏树上的父亲查询的. 另开一个并查集,在并查集上进行路径压缩的查询即可. Code: #include <cstdio> #include <algorithm> #include <cstring> #define maxn 100006 #define setIO(s) freopen(s".in","r",stdin) using namespa

模板 - 左偏树 + 并查集

这两个经常混在一起用的样子,封成同一个好了. #include<bits/stdc++.h> using namespace std; typedef long long ll; int solve(); int main() { #ifdef Yinku freopen("Yinku.in","r",stdin); #endif // Yinku solve(); } int n,m; const int MAXN=100005; int tot,v[

P3377 【模板】左偏树(可并堆) 左偏树浅谈

因为也是昨天刚接触左偏树,从头理解,如有不慎之处,跪请指教. 左偏树: 什 么是(fzy说)左偏树啊? 前置知识: 左偏树中dist:表示到右叶点(就是一直往右下找,最后一个)的距离,特别的,无右节点的为0. 堆:左偏树是个堆. 关于左偏性质:可以帮助堆合并(研究深了我也不懂的,看代码理解) 对于任意的节点,dist[leftson]>=dist[rightson],体现了左偏性质. 同理可得:对于任意右儿子的父亲节点的dist自然等于右儿子的dist+1喽 关于各种操作: merge: 是插入

Luogu P3377【模板】左偏树(可并堆)

采用了jls的左偏树写法,非常好写... 额外用一个并查集维护集合关系即可,注意路径压缩. #include <bits/stdc++.h> using namespace std; typedef long long LL; #define LOG(...) fprintf (stderr, __VA_ARGS__) #define pb push_back #define mp make_pair #define SZ(x) ((int)(x).size()) #define ALL(x)