zoukankan      html  css  js  c++  java
  • 左偏树学习笔记

    学习材料:https://wenku.baidu.com/view/029c886d1eb91a37f1115ce5.html

    例题1:bzoj 2809 [Apio2012]dispatching

      题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2809

      枚举每个点作为管理者,那么就是子树里选一些 c 最小的,看看最多能选即可。

      想到线段树合并,维护区间 c 的和、个数,然后线段树上二分。

      可以用左偏树。维护大根堆,当堆里元素代价总和大于限制时,弹掉代价最大的一些元素。(注意是一些元素而不是一个)

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    int rdn()
    {
      int ret=0;bool fx=1;char ch=getchar();
      while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
      while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
      return fx?ret:-ret;
    }
    ll Mx(ll a,ll b){return a>b?a:b;}
    const int N=1e5+5;
    int n,m,vl[N],l[N],hd[N],xnt,to[N<<1],nxt[N<<1];
    int rt[N],s[N],ct[N],dis[N],ls[N],rs[N],dep[N]; ll ans;
    void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;}
    int mrg(int x,int y)
    {
      if(!x||!y)return x|y;
      if(vl[x]<vl[y])swap(x,y);
      rs[x]=mrg(rs[x],y);
      if(dis[rs[x]]>dis[ls[x]])swap(ls[x],rs[x]);
      dis[x]=dis[rs[x]]+1;
      return x;///
    }
    int del(int x)
    {
      return mrg(ls[x],rs[x]);
    }
    void dfs(int cr,int fa)
    {
      rt[cr]=cr; s[cr]=vl[cr]; ct[cr]=1;
      for(int i=hd[cr],v;i;i=nxt[i])
        if((v=to[i])!=fa)
          {
        dfs(v,cr); rt[cr]=mrg(rt[cr],rt[v]);
        s[cr]+=s[v]; ct[cr]+=ct[v];
        while(s[cr]>m)//while!! not if
          {
            s[cr]-=vl[rt[cr]]; ct[cr]--;
            rt[cr]=del(rt[cr]);
          }
          }
      ans=Mx(ans,(ll)l[cr]*ct[cr]);
    }
    int main()
    {
      n=rdn(); m=rdn(); dis[0]=-1;//
      for(int i=1,d;i<=n;i++)
        {
          d=rdn();if(i>1)add(d,i);
          vl[i]=rdn(); l[i]=rdn();
        }
      dfs(1,0);
      printf("%lld
    ",ans);
      return 0;
    }
    View Code

     例题2:洛谷 3377 模板左偏树

      题目:https://www.luogu.org/problemnew/show/P3377

      真是模板。

      同时再用一个并查集维护每个点在哪个堆即可。有删点,不用改变并查集的结构,只需给每个并查集的根记录一下该点集对应的左偏树的根是谁即可。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int rdn()
    {
      int ret=0;bool fx=1;char ch=getchar();
      while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
      while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
      return fx?ret:-ret;
    }
    const int N=1e5+5;
    int n,m,fa[N],rt[N],dis[N],ls[N],rs[N];bool vis[N];
    struct Node{
      int x,y;
      bool operator< (const Node &b)const
      {return x==b.x?y<b.y:x<b.x;}
    }a[N];
    int fnd(int a){return fa[a]==a?a:fa[a]=fnd(fa[a]);}
    int mrg(int x,int y)
    {
      if(!x||!y)return x|y;
      if(a[y]<a[x])swap(x,y);
      rs[x]=mrg(rs[x],y);
      if(dis[rs[x]]>dis[ls[x]])swap(ls[x],rs[x]);
      dis[x]=dis[rs[x]]+1;
      return x;
    }
    int del(int x)
    {
      return mrg(ls[x],rs[x]);
    }
    int main()
    {
      n=rdn(); m=rdn(); dis[0]=-1;//
      for(int i=1;i<=n;i++)
        {
          fa[i]=i; rt[i]=i;
          a[i].x=rdn(); a[i].y=i;
        }
      for(int i=1,op,x,y;i<=m;i++)
        {
          op=rdn();
          if(op==1)
        {
          x=rdn();y=rdn();
          if(vis[x]||vis[y])continue;
          x=fnd(x); y=fnd(y); if(x==y)continue;
          fa[y]=x; rt[x]=mrg(rt[x],rt[y]);
        }
          else
        {
          x=rdn(); if(vis[x]){puts("-1");continue;}
          x=fnd(x); printf("%d
    ",a[rt[x]].x);
          vis[rt[x]]=1; rt[x]=del(rt[x]);
        }
        }
      return 0;
    }
    View Code
  • 相关阅读:
    R语言代写模型中的加总偏误与内生性:一种数值模拟方法
    R语言代写NYPD纽约市警察局抢劫者数据分析
    R语言代写使用马尔可夫链Markov Chain, MC来模拟抵押违约
    BZOJ 2600: [Ioi2011]ricehub 双指针+贪心
    BZOJ 4903: [Ctsc2017]吉夫特 数论+dp
    BZOJ 4500: 矩阵 带权并查集
    草稿
    BZOJ 5082: 弗拉格 矩阵乘法
    BZOJ 4764: 弹飞大爷 LCT
    BZOJ 3309: DZY Loves Math 莫比乌斯反演+打表
  • 原文地址:https://www.cnblogs.com/Narh/p/11038423.html
Copyright © 2011-2022 走看看