洛谷 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\n",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\n",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\n",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\n",ans);
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/Narh/p/10666680.html
今日推荐