POJ 2057 The Lost House

vjudge

智商掉线*2...

可以看成求一种遍历叶子的顺序,使得以每个叶子为终点的路径的长度之和最小.考虑设\(f_x\)表示\(x\)子树内,以\(x\)为起点到所有叶子的路径长度之和的最小值,\(g_x\)表示从\(x\)父亲走进子树\(x\)然后遍历完一遍后出来的步数(也就是没走进终点所在子树而要浪费的步数),\(s_x\)表示\(x\)子树内叶子节点个数

先考虑\(g_x\),如果没有worm那么就是子树大小\(*2\).否则因为worm会告诉snail终点不在这个子树内,那么要遍历的联通块大小就是所有worm节点断掉儿子的边以后的联通块大小,\(g_x\)就是这个联通块大小\(*2\)

然后是\(f_x\),因为终点可能在所有儿子子树内,所以后遍历的子树的步数要加上前面遍历子树浪费的步数\(g_y\),转移要枚举遍历儿子的顺序,然后大概长这样\[f_x=(\sum_{y\in son\_of\_x}f_y )+(\min_{\{p1,p2,p3...p_{cnt}\}=son\_of\_x}\sum_{i=1}^{cnt}s_{p_i}*(1+\sum_{j=1}^{i-1}g_{p_j}))\]

考虑后半部分,这个式子有点国王游戏,考虑交换两个相邻儿子的枚举顺序来优化答案,首先这不会对其他的儿子贡献产生影响,然后如果\(i\)号放\(i+1\)号儿子前面更优,相当于这种情况的和比\(i\)放后面的和要小,进一步化简可以得到要满足\(s_ig_{i+1}>s_{i+1}g_i\)这个条件,所以可以按照这个为关键字排序,然后一遍扫过去得到\(f_x\).最后输出\(\frac{f_{root}}{s_{root}}\)

#include<bits/stdc++.h>
//poj 不能用bits头,请选c++编译器
#define LL long long
#define uLL unsigned long long
#define db double

using namespace std;
const int N=1000+10;
int rd()
{
    int x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
int to[N<<1],nt[N<<1],hd[N],tot=1;
void add(int x,int y)
{
    ++tot,to[tot]=y,nt[tot]=hd[x],hd[x]=tot;
    ++tot,to[tot]=x,nt[tot]=hd[y],hd[y]=tot;
}
char cc[N];
bool v[N],bb[N];
int n,rt,sz[N],ss[N],st[N],tp;
bool cmp(int aa,int bb){return 1ll*ss[aa]*sz[bb]>1ll*ss[bb]*sz[aa];}
LL f[N];
void dp(int x,int ffa)
{
    sz[x]=1,ss[x]=f[x]=0;
    if(ffa&&!nt[hd[x]]){ss[x]=1;return;}
    for(int i=hd[x];i;i=nt[i])
    {
        int y=to[i];
        if(y==ffa) continue;
        dp(y,x),ss[x]+=ss[y];
        if(!v[x]) sz[x]+=sz[y];
    }
    tp=0;
    for(int i=hd[x];i;i=nt[i])
    {
        int y=to[i];
        if(y==ffa) continue;
        st[++tp]=y,f[x]+=f[y]+ss[y];
    }
    sort(st+1,st+tp+1,cmp);
    LL dt=0;
    for(int i=1;i<=tp;++i)
    {
        int y=st[i];
        f[x]+=ss[y]*dt;
        dt+=sz[y]*2;
    }
}

int main()
{
    int T=rd();
    while(T--)
    {
        n=rd();
        for(int i=1;i<=n;++i) hd[i]=0;
        tot=1;
        for(int i=1;i<=n;++i)
        {
            int y=rd();
            if(y>0) add(i,y);
            else rt=i;
            scanf("%s",cc);
            v[i]=cc[0]=='Y';
        }
        dp(rt,0);
        printf("%.4lf\n",f[rt]/(db)ss[rt]);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/smyjr/p/11435485.html
今日推荐