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; }