bzoj1455罗马游戏
【问题描述】
罗马皇帝很喜欢玩杀人游戏。 他的军队里面有n个人,每个人都是一个独立的团。最近举行了一次平面几何测试,每个人都得到了一个分数。 皇帝很喜欢平面几何,他对那些得分很低的人嗤之以鼻。他决定玩这样一个游戏。 它可以发两种命令: 1. Merger(i, j)。把i所在的团和j所在的团合并成一个团。如果i, j有一个人是死人,那么就忽略该命令。 2. Kill(i)。把i所在的团里面得分最低的人杀死。如果i这个人已经死了,这条命令就忽略。 皇帝希望他每发布一条kill命令,下面的将军就把被杀的人的分数报上来。(如果这条命令被忽略,那么就报0分)
【问题分析】
很明显的左偏堆(树)裸题。我们需要维护一个数据结构能在O(n*logn)内查询最小值以及支持合并操作。那么我们来介绍一下左偏堆。先说一下什么是堆。就是一个类似于二叉搜索树的结构(感觉就是啊),对于每个节点都满足它的俩个子节点小(大)于它本身的权值。但是使用过程中会遇到一个问题,可以构造出特定的序列,使得后加入的点形成一条链,这样的话进行某些操作时,堆就不能有很快的速度了。所以我们用一个类似于维护平衡树的方法来实现左偏堆,每次插入新的元素都插入到节点的右儿子中,然后维护一个深度域,如果发现右儿子深度比左儿子大就swap左右孩子,即可期望深度保证logn层。
#include<bits/stdc++.h>
using namespace std;
const int maxn=10005;
typedef struct node node;
struct node{//值,左右儿编号,老豆(根)编号,死亡标记(1已死),深度标记(用于合并时得到新根后调整左右儿)
int val;int l;int r;int fa;int die;int dep;
}s[maxn];
int fid(int x){//找X的
if(s[x].fa!=x)s[x].fa=fid(s[x].fa);
return s[x].fa;
}
int merge(int x,int y){//传入合并的就是两个根
if(!x||!y)return x+y;//如果有一个是0表示空(一个叶子根点的左右儿就是0,全局变量没有初始化默认0)
if(s[x].val>s[y].val)swap(s[x].val,s[y].val);//要把X提为根所以X要保证较小
s[x].r=merge(s[x].r,y);//合并X的右儿与新根Y
if(s[s[x].l].dep<s[s[x].r].dep)swap(s[x].l,s[x].r);//左儿比右儿浅就掉换以实现平衡(因为每次都是右子树与新根合并所以左儿肯定浅)
s[x].dep=s[s[x].r].dep+1;//X的深度更新为右儿的深度+1
return x;//返回新根
}
int main(){
int n;cin>>n;//N个点
for(int i=1;i<=n;i++)cin>>s[i].val;//输入值
for(int i=1;i<=n;i++)s[i].fa=i;//老豆是自身
int m;cin>>m;//M个操作
for(int i=1;i<=m;i++){//遍历
char ch;cin>>ch;//读入操作符
if(ch=='M'){//MERGE就是合并
int x,y;cin>>x>>y;//读入合并的两个点
if(s[x].die||s[y].die)continue;//如果有一个是死了就跳出
int f1=fid(x),f2=fid(y);//否则读出两个根
if(f1==f2)continue;//同根就跳出
int f3=merge(f1,f2);//否则就合并返回新根
s[f1].fa=s[f2].fa=f3;//两个结点的老豆都记为这个新根
}
else{//删除结点
int x;cin>>x;//读出删除的结点的编号
if(s[x].die==1)cout<<'0'<<endl;//已死就输出0
else{//未死
int f1=fid(x);//读出其根
cout<<s[f1].val<<endl;//输出其值
int f2=merge(s[f1].l,s[f1].r);//合并左右儿子都得到新根
s[s[f1].l].fa=s[s[f1].r].fa=f2;//新根是原来左右儿子的老豆(新根就是左右儿中的一个)
s[f1].fa=f2;//已死的F1老豆也要指向新根,因为树下有很多结点的FA还是指向F1的!
}//上面最后一句是关键!!!
}
}
return 0 ;
}
/*
Sample Input
5
100 90 66 99 10
7
M 1 5
K 1
K 1
M 2 3
M 3 4
K 5
K 4
Sample Output
10
100
0
66
*/
下面是数组版
#include<bits/stdc++.h>
using namespace std;
const int maxn=10005;
int a[maxn],l[maxn],r[maxn],fa[maxn],die[maxn],dep[maxn];
int fid(int x){
if(fa[x]!=x)fa[x]=fid(fa[x]);
return fa[x];
}
int merge(int x,int y){//传入合并的就是两个根
if(!x||!y)return x+y;
if(a[x]>a[y])swap(x,y);
r[x]=merge(r[x],y);
if(dep[l[x]]<dep[r[x]])swap(l[x],r[x]);
dep[x]=dep[r[x]]+1;
return x;
}
int main(){
int n;cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)fa[i]=i;
int m;cin>>m;
for(int i=1;i<=m;i++){
char ch;cin>>ch;
if(ch=='M'){
int x,y;cin>>x>>y;
if(die[x]||die[y])continue;
int f1=fid(x),f2=fid(y);
if(f1==f2)continue;
int f3=merge(f1,f2);
fa[f1]=fa[f2]=f3;
}
else{
int x;cin>>x;
if(die[x]==1)cout<<'0'<<endl;
else{
int f1=fid(x);
die[f1]=1;
cout<<a[f1]<<endl;
int f2=merge(l[f1],r[f1]);
fa[f2]=f2;
fa[f1]=f2//this is the key point ;
}
}
}
return 0 ;
}