• 洛谷 5290 [十二省联考2019]春节十二响——堆


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

    考场上想到了一个子树里如果有多个 “段” 准备和其他位置的 “段” 拼在一起,那么这个子树里的这些 “段” 一定两两间互相有父子关系。

    准备设计一个 DP ,但觉得很难弄。比如很难存下状态,因为还要存 “有几个待合并的“段”” 、“那些“段”的最大值是什么” 之类的。所以就只写了 60 分。

    #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;}
    ll Mn(ll a,ll b){return a<b?a:b;}
    const int N=2e5+5;
    int n,hd[N],xnt,to[N],nxt[N],cd[N],c[N];
    void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;cd[x]++;}
    namespace S1{
      const int K=20,M=(1<<16)+5; const ll INF=2e10;
      int tim,dfn[K],ot[K],bin[K],t[M],tp[K];ll dp[M];
      void ini_dfs(int cr)
      {
        dfn[cr]=++tim;
        for(int i=hd[cr];i;i=nxt[i])
          ini_dfs(to[i]);
        ot[cr]=tim;
      }
      bool chk(int x,int y)
      { return (dfn[x]>=dfn[y]&&dfn[x]<=ot[y])||(dfn[y]>=dfn[x]&&dfn[y]<=ot[x]);}
      void solve()
      {
        ini_dfs(1); bin[0]=1;
        for(int i=1;i<=n;i++)bin[i]=bin[i-1]<<1;
        for(int s=0;s<bin[n];s++)
          {
        int tot=0,mx=0; bool fg=0;
        for(int i=1;i<=n;i++)
          if(s&bin[i-1])
            {
              for(int j=1;j<=tot;j++)
            if(chk(i,tp[j])){fg=1;break;}
              if(fg)break; tp[++tot]=i; mx=Mx(mx,c[i]);
            }
        if(!fg)t[s]=mx;
          }
        for(int s=1;s<bin[n];s++)
          {
        dp[s]=(t[s]?t[s]:INF);
        for(int d=(s-1)&s;d;d=(d-1)&s)
          dp[s]=Mn(dp[s],dp[d]+dp[s^d]);
          //if(t[d]) dp[s]=Mn(dp[s],t[d]+dp[s^d]);
          }
        printf("%lld
    ",dp[bin[n]-1]);
      }
    }
    namespace S2{
      int a[N],b[N],ta,tb;ll ans;
      bool cmp(int u,int v){return u>v;}
      void dfsa(int cr,int fa)
      {
        a[++ta]=c[cr];
        for(int i=hd[cr],v;i;i=nxt[i])
          if((v=to[i])!=fa) dfsa(v,cr);
      }
      void dfsb(int cr,int fa)
      {
        b[++tb]=c[cr];
        for(int i=hd[cr],v;i;i=nxt[i])
          if((v=to[i])!=fa) dfsb(v,cr);
      }
      void solve()
      {
        if(cd[1]==1)
          {
        for(int i=1;i<=n;i++)ans+=c[i];
        printf("%lld
    ",ans); return;
          }
        dfsa(to[hd[1]],1); dfsb(to[nxt[hd[1]]],1);
        sort(a+1,a+ta+1,cmp); sort(b+1,b+tb+1,cmp);
        for(int i=1,lm=Mn(ta,tb);i<=lm;i++)
          ans+=Mx(a[i],b[i]);
        if(ta<tb){for(int i=ta+1;i<=tb;i++)ans+=b[i];}
        else {for(int i=tb+1;i<=ta;i++)ans+=a[i];}
        printf("%lld
    ",ans+c[1]);
      }
    }
    int main()
    {
      freopen("spring.in","r",stdin);
      freopen("spring.out","w",stdout);
      n=rdn();
      for(int i=1;i<=n;i++)c[i]=rdn();
      for(int i=2,d;i<=n;i++)
        d=rdn(), add(d,i);
      if(n<=16){S1::solve();return 0;}
      bool fg=0;
      for(int i=2;i<=n;i++)
        if(cd[i]>1){fg=1;break;}
      if(!fg&&cd[1]<=2){S2::solve();return 0;}
      return 0;
    }

    其实从 “链” 的部分受到启发,如果两个部分互相没有父子关系,就是把最大值和最大值放在一段,次大值和次大值放在一段这样贪心。

    那么在树上也可以贪心(而不是 DP ),不用管 “有几个待合并的段” ,直接把子树里的所有已有的 “段” 都视作待合并的,那么就用大根堆维护子树里的 “段”。在 dfs 树的时候,合并两个子树就用堆的启发式合并即可。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #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;
    }
    int Mx(int a,int b){return a>b?a:b;}
    int Mn(int a,int b){return a<b?a:b;}
    const int N=2e5+5;
    int n,c[N],hd[N],xnt,to[N],nxt[N],rt[N],tot,tp[N];
    priority_queue<int> q[N];
    void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;}
    void dfs(int cr)
    {
      for(int i=hd[cr],v;i;i=nxt[i])
        {
          dfs(v=to[i]);
          if(q[rt[cr]].size()<q[rt[v]].size())
        swap(rt[cr],rt[v]);
          int r0=rt[cr], r1=rt[v], top=0;
          while(q[r1].size())
        {
          int c0=q[r0].top(), c1=q[r1].top();
          q[r0].pop(); q[r1].pop();
          tp[++top]=Mx(c0,c1);
        }
          for(int j=1;j<=top;j++)q[r0].push(tp[j]);
        }
      if(!rt[cr])rt[cr]=++tot;
      q[rt[cr]].push(c[cr]);
    }
    int main()
    {
      n=rdn();
      for(int i=1;i<=n;i++)c[i]=rdn();
      for(int i=2,d;i<=n;i++)
        d=rdn(), add(d,i);
      dfs(1); ll ans=0; int cr=rt[1];
      while(q[cr].size())ans+=q[cr].top(), q[cr].pop();
      printf("%lld
    ",ans);
      return 0;
    }
  • 相关阅读:
    vi 批量加注释与替换
    linux常用快捷键
    kubectl常用命令
    软件管理方法
    zabbix3.2升级3.4报错Database error
    zabbix显示中文乱码
    主从复制延迟及原因
    Python-- 文件与操作系统
    层次聚类
    盲源分离
  • 原文地址:https://www.cnblogs.com/Narh/p/10666680.html
Copyright © 2020-2023  润新知