BZOJ2491 : Quelling Blade

首先将合成树展开,得到一棵不超过$m(m\leq 10^6)$的有根树。

问题等价于,不休息地访问所有点,访问每个点需要时间$t_i$,价值为$v_i$。

设$vis_i$为访问$i$点的时间(不含$t_i$),最大化$\sum t_i\times v_i$。

根据排序不等式可得,需要按照$\frac{v_i}{t_i}$从小到大的顺序访问最优。

用堆维护所有非根节点,每次取出最优节点,将其与父亲合并,用并查集维护每个点的父亲,直到只剩根节点为止。

时间复杂度$O(m\log m)$。

 

#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
typedef pair<double,P>PI;
const int N=1010,M=1000010;
int T,C,n,m,i,k,x,y,g[N],v[N],w[N],ed,nxt[N],vis[M],f[M];ll ans,st[M],sv[M];bool del[M];
struct E{int v,t;}a[N];
priority_queue<PI,vector<PI>,greater<PI> >q;
inline void add(int x,int y,int z){v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed;}
void dfs(int x,int y){
  int o=++m;
  f[o]=y,vis[o]=o,del[o]=0;
  sv[o]=a[x].v,st[o]=a[x].t;
  if(o>1)q.push(PI(1.0*sv[o]/st[o],P(o,o)));
  for(int i=g[x];i;i=nxt[i])for(int j=1;j<=w[i];j++)dfs(v[i],o);
}
int F(int x){return del[f[x]]?f[x]=F(f[x]):f[x];}
int main(){
  scanf("%d",&T);
  for(C=1;C<=T;C++){
    scanf("%d",&n);
    for(ans=m=ed=0,i=1;i<=n;i++)g[i]=0;
    for(i=1;i<=n;i++){
      scanf("%d%d%d",&a[i].v,&a[i].t,&k);
      while(k--)scanf("%d%d",&x,&y),add(i,x,y);
    }
    dfs(1,0);
    while(!q.empty()){
      PI t=q.top();q.pop();
      if(del[x=t.second.first])continue;
      if(vis[x]!=t.second.second)continue;
      del[x]=1;
      y=F(x);
      ans+=st[y]*sv[x];
      st[y]+=st[x],sv[y]+=sv[x];
      if(y>1)q.push(PI(1.0*sv[y]/st[y],P(y,vis[y]=++m)));
    }
    printf("Case #%d: %lld\n",C,ans);
  }
  return 0;
}

  

posted @ 2017-07-11 19:02  Claris  阅读(242)  评论(0编辑  收藏  举报