多叉树转二叉树


问题描述:

一棵有根树,规定根节点深度为 0 ,其他节点深度等于父亲的深度 +1 。

有一棵多叉树,你需要把它按照“左儿子右兄弟”的规则转化为二叉树。

设节点 x 转化前后深度分别为 d1[x],d2[x] ,

则转化的代价为∑∣d1[x]?d2[x]∣

请你分别求出最小代价和最大代价。

 分析:

考察树形dp,贪心。

$f[x]$表示以$x$为根的子树转换后的最大代价。

f[x]=sigma(f[v])+sz[v1]*0+sz[v2]*1+sz[v3]*2....

$sz[x]$表示$x$的子树中的节点个数。

当$sz[x]$从小到大排好序,便可贪心求出答案。

#include
using namespace std;
#define re register int
#define LL long long

static char buf[1<<20], *p1=buf, *p2=buf;
#define getchar() p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<20, stdin), p1==p2)?EOF:*p1++
inline int read()
{
    int x=0, flag=0;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-')flag=1;
        c=getchar();
    }
    while('0'<=c&&c<='9')x=(x<<1)+(x<<3)+c-48,c=getchar();
    return flag?-x:x;
}

const int N=1e6+5;

int sz[N];
LL f[N], g[N];
int tt, las[N], ed[N<<1], nt[N<<1];
void add(int x, int y){
    ed[++tt]=y;nt[tt]=las[x];las[x]=tt;
}

int tmp[N];
void DFS(int x, int fa)
{
    int son = 0;
    sz[x] = 1;
    for(re i=las[x];i;i=nt[i])
    {
        int v=ed[i];
        if(v != fa)
        {
            DFS(v, x);
            sz[x] += sz[v];
            f[x] += f[v];
            g[x] += g[v];
        }
    }
    
    for(re i=las[x];i;i=nt[i])
    {
        int v=ed[i];
        if(v != fa)
        tmp[++son]=sz[v];
    }
    sort(tmp+1, tmp+1+son);
    for(re i=1;i<=son;++i)
    {
        f[x]+=1ll*(i-1)*tmp[i];
        g[x]+=1ll*(son-i)*tmp[i];
    }
}
int main()
{
    int n=read();
    for(re i=1;ii)
    {
        int x=read(), y=read();
        add(x, y);
        add(y, x);
    }
    DFS(1, 0);
    printf("%lld %lld", g[1], f[1]);
    return 0;
}