BZOJ1455: 罗马游戏
【传送门:BZOJ1455】
简要题意:
罗马皇帝很喜欢玩杀人游戏。 他的军队里面有n个人,每个人都是一个独立的团。最近举行了一次平面几何测试,每个人都得到了一个分数。 皇帝很喜欢平面几何,他对那些得分很低的人嗤之以鼻。他决定玩这样一个游戏。 它可以发两种命令: 1. Merger(i, j)。把i所在的团和j所在的团合并成一个团。如果i, j有一个人是死人,那么就忽略该命令。 2. Kill(i)。把i所在的团里面得分最低的人杀死。如果i这个人已经死了,这条命令就忽略。 皇帝希望他每发布一条kill命令,下面的将军就把被杀的人的分数报上来。(如果这条命令被忽略,那么就报0分)
题解:
左偏树的模版题
左偏树就是合并堆
性质:
一、节点的关键值大于等于两个儿子节点的关键值(堆的性质)
二、定义节点到最近的叶节点的距离为节点距离,任意节点的左儿子的距离大于右儿子的距离(左偏树的性质)
左偏树在实现插入操作时总是从右侧插入,也就是总是让短的一侧生长,如果右侧长于左侧,那么交换左右侧,继续从右侧生长
综上,都是hzwer(orz)的原话
参考代码:
#include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> #include<cmath> using namespace std; int fa[1100000],l[1100000],r[1100000],dep[1100000],d[1100000]; bool v[1100000]; int findfa(int x) { if(x!=fa[x]) fa[x]=findfa(fa[x]); return fa[x]; } int merge(int x,int y) { if(x==0) return y; if(y==0) return x; if(d[x]>d[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; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&d[i]); for(int i=1;i<=n;i++) fa[i]=i; memset(v,true,sizeof(v)); dep[0]=-1; int m; scanf("%d",&m); for(int i=1;i<=m;i++) { char st[2]; scanf("%s",st+1); if(st[1]=='M') { int x,y; scanf("%d%d",&x,&y); if(v[x]==false||v[y]==false) continue; int fx=findfa(x),fy=findfa(y); if(fx!=fy) fa[fx]=fa[fy]=merge(fx,fy); } else { int x; scanf("%d",&x); if(v[x]==false){printf("0\n");continue;} int fx=findfa(x); printf("%d\n",d[fx]); v[fx]=false; fa[fx]=merge(l[fx],r[fx]); fa[fa[fx]]=fa[fx]; } } return 0; }
渺渺时空,茫茫人海,与君相遇,幸甚幸甚