左偏树(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 }