左偏树

bzoj1455 罗马游戏
Description
罗马皇帝很喜欢玩杀人游戏。 他的军队里面有n个人,每个人都是一个独立的团。最近举行了一次平面几何测试,每个人都得到了一个分数。 皇帝很喜欢平面几何,他对那些得分很低的人嗤之以鼻。他决定玩这样一个游戏。 它可以发两种命令: 1. Merger(i, j)。把i所在的团和j所在的团合并成一个团。如果i, j有一个人是死人,那么就忽略该命令。 2. Kill(i)。把i所在的团里面得分最低的人杀死。如果i这个人已经死了,这条命令就忽略。 皇帝希望他每发布一条kill命令,下面的将军就把被杀的人的分数报上来。(如果这条命令被忽略,那么就报0分)
Input
第一行一个整数n(1<=n<=1000000)。n表示士兵数,m表示总命令数。 第二行n个整数,其中第i个数表示编号为i的士兵的分数。(分数都是[0..10000]之间的整数) 第三行一个整数m(1<=m<=100000) 第3+i行描述第i条命令。命令为如下两种形式: 1. M i j 2. K i
Output
如果命令是Kill,对应的请输出被杀人的分数。(如果这个人不存在,就输出0)

#include
#include
#include
#include
using namespace std;
const int maxn=1000010;
int n,m;
int fa[maxn],die[maxn];
int val[maxn],lef[maxn],rig[maxn],dis[maxn];
int father(int u)
{
    while(fa[u]!=u)
    {
        fa[u]=fa[fa[u]];
        u=fa[u];
    }
    return u;
}
int merge(int x,int y)
{
    if(!x) return y;
    if(!y) return x;
    if(val[x]>val[y]) swap(x,y);
    rig[x]=merge(rig[x],y);
    fa[rig[x]]=x;//并查
    if(dis[rig[x]]>dis[lef[x]]) swap(lef[x],rig[x]);
    dis[x]=dis[rig[x]]+1;//如果x没有右孩子,则rig[x]=0,而dis[0]=-1,则dis[x]=0;
    return x;
}
int main()
{
    int u,v,fu,fv,t;
      scanf("%d",&n);
      for(int i=1;i<=n;i++)
      {
          scanf("%d",val+i);
          fa[i]=i;
          dis[i]=0;
          die[i]=0;
          lef[i]=0;
          rig[i]=0;
      }
      dis[0]=-1;
      char op[3];
      scanf("%d",&m);
      for(int i=1;i<=m;i++)
      {
          scanf("%s",op);
          if(op[0]=='M')
          {
            scanf("%d%d",&u,&v);
            if(die[u]||die[v]) continue;
            fu=father(u);
            fv=father(v);
            if(fu==fv) continue;
            t=merge(fu,fv);
            fa[t]=t;//成为根节点
          }
          else{

            scanf("%d",&v);
            if(die[v]) {
                printf("0\n");
            }
            else{
                fv=father(v);
                die[fv]=1;
                printf("%d\n",val[fv]);
                t=merge(lef[fv],rig[fv]);
                fa[t]=t;//成为根节点
            }
          }
      }
}

https://vjudge.net/problem/HDU-1512
题意:有n个猴子,一开始每个猴子只认识自己。每个猴子有一个力量值,力量值越大表示这个猴子打架越厉害。如果2个猴子不认识,他们就会找他们认识的猴子中力量最大的出来单挑,单挑不论输赢,单挑的2个猴子力量值减半,这2拨猴子就都认识了,不打不相识嘛。现在给m组询问,如果2只猴子相互认识,输出-1,否则他们各自找自己认识的最牛叉的猴子单挑,求挑完后这拨猴子力量最大值。
题解 :左偏树+并查集

#include
#include
#include
#include
using namespace std;
const int maxn=100010;
int n,m;
int fa[maxn];
int val[maxn],lef[maxn],rig[maxn],dis[maxn];
int father(int u)
{
    while(fa[u]!=u)
    {
        fa[u]=fa[fa[u]];
        u=fa[u];
    }
    return u;
}
int merge(int &x,int &y)
{
    if(!x) return y;
    if(!y) return x;
    if(val[x]dis[lef[x]]) swap(lef[x],rig[x]);
    dis[x]=dis[rig[x]]+1;//如果x没有右孩子,则rig[x]=0,而dis[0]=-1,则dis[x]=0;
    return x;
}
int pop(int &t)
{
    int l=lef[t],r=rig[t];
    fa[l]=l;//因为要暂时删掉根,所以左右子树先作为根
    fa[r]=r;
    lef[t]=rig[t]=dis[t]=0;
    return merge(l,r);
}
int main()
{
    int u,v,fu,fv,a,b,c;
    while(scanf("%d",&n)!=EOF){

      for(int i=1;i<=n;i++)
      {
          scanf("%d",val+i);
          fa[i]=i;
          dis[i]=0;
          lef[i]=0;
          rig[i]=0;
      }
      dis[0]=-1;
      scanf("%d",&m);
      for(int i=1;i<=m;i++)
      {
            scanf("%d%d",&u,&v);
            fu=father(u);
            fv=father(v);
            if(fu==fv)
            {
                printf("-1\n");
                continue;
            }
            val[fu]>>=1;
            val[fv]>>=1;
            a=pop(fu);
            b=pop(fv);
            c=merge(a,b);
            c=merge(fu,c);
            c=merge(fv,c);
            printf("%d\n",val[c]);
      }
    }
}

https://vjudge.net/problem/HDU-3031
题意:喜羊羊和灰太狼比较所持有的卡片的大小,每张卡片上都有一定的点数,有5种操作,如下:

  1. T K: 拿到第 k 堆所有牌
  2. C: 喜羊羊和灰太狼手中最大的牌进行比较,赢得一方可以把对方的所有牌全部取过来
  3. L: 失去手中最大的一张牌
  4. A P: 手中最大的牌点数加上P
  5. E Q: 手中最大的牌点数改为Q点
    共有R轮比赛,每轮都是双方轮流操作,灰太狼先抽,最后输出输赢即可。
#include
#include
#include
using namespace std;
const int maxn=1000010;
int val[maxn],lef[maxn],rig[maxn],dis[maxn];
int merge(int &x,int &y)
{
    if(!x) return y;
    if(!y) return x;
    if(val[x]=val[root[1]])
                {
                    root[0]=merge(root[0],root[1]);
                    sum[0]+=sum[1];
                    root[1]=0;
                    sum[1]=0;
                }
                else
                {
                     root[1]=merge(root[0],root[1]);
                    sum[1]+=sum[0];
                    root[0]=0;
                    sum[0]=0;
                }
            }
             else if(op[0]=='L')
            {
                root[i&1]=pop(root[i&1]);
                sum[i&1]--;
            }
             else if(op[0]=='A')
            {
                scanf("%d",&v);
                val[root[i&1]]+=v;
            }
             else
            {
                scanf("%d",&v);
                int t=pop(root[i&1]);
                val[root[i&1]]=v;
                root[i&1]=merge(t,root[i&1]);
            }
        }
        printf("%d:%d\n",sum[0],sum[1]);
        if(sum[0]>=sum[1]) Wolffy++;
        else Happy++;
    }
    if(Wolffy>Happy) printf("Hahaha...I win!!\n");
    else printf("I will be back!!\n");
}

你可能感兴趣的:(左偏树)