【PA2015】【BZOJ4297】Rozstaw szyn

Description

给定一棵有n个点,m个叶子节点的树,其中m个叶子节点分别为1到m号点,每个叶子节点有一个权值r[i]。你需要给剩下n-m个点各指定一个权值,使得树上相邻两个点的权值差的绝对值之和最小。

Input

第一行包含两个正整数n,m(2<=n<=500000,1<=m<=n),分别表示点数和叶子数。
接下来n-1行,每行两个正整数u,v(1<=u,v<=n),表示u与v之间有一条边。
接下来m行,每行一个正整数,依次为r[1],r[2],…,rm,表示每个叶子的权值。

Output

输出一个整数,即树上相邻两个点的权值差的绝对值之和的最小值。

Sample Input

6 4

1 5

2 5

3 6

4 6

5 6

5

10

20

40
Sample Output

35
HINT

Source

By Claris

这题不会做..后来看了Claris题解..
大致就是拆掉叶子点,重构一下图,然后可以从孩子逆推到父亲节点的取值范围.
Claris的题解

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 500010
#define LL long long
#define GET (ch>='0'&&ch<='9')
using namespace std;
int n,m,top,tp;
int c,head=1,tail;
LL ans,sum,tmp,now;
int val[MAXN],r[MAXN],del[MAXN],q[MAXN];
int sta[MAXN<<1],cnt;
int f[MAXN],d[MAXN];
inline int abs(int x)   {   return x<0?-x:x;    }
struct edge
{
    int to;
    edge *next;
}e[MAXN<<1],*prev[MAXN],E[MAXN],*Prev[MAXN];
inline void insert(int u,int v) {   d[u]++;e[++top].to=v;e[top].next=prev[u];prev[u]=&e[top];   }
inline void Insert(int u,int v) {   f[v]=u;E[++tp].to=v;E[tp].next=Prev[u];Prev[u]=&E[tp];  }
void in(int &x)
{
    char ch=getchar();x=0;
    while (!GET)    ch=getchar();
    while (GET) x=x*10+ch-'0',ch=getchar();
}
void dfs(int x)
{
    if (!Prev[x])   return;
    for (edge *i=Prev[x];i;i=i->next)   dfs(i->to);
    tmp=1ll<<62;sum=0;cnt=0;c=0;
    for (edge *i=Prev[x];i;i=i->next)   sta[++cnt]=val[i->to],sta[++cnt]=r[i->to],c--,sum+=val[i->to];
    sort(sta+1,sta+cnt+1);
    for (int i=1;i<=cnt;i++)
    {
        c++;sum-=sta[i];now=sum+1ll*sta[i]*c;
        if (now<tmp)    tmp=now,val[x]=sta[i];
        if (now==tmp)   r[x]=sta[i];
    }
    ans+=tmp;
}
int main()
{
    in(n);in(m);int u,v;
    for (int i=1;i<n;i++)   in(u),in(v),insert(u,v),insert(v,u);
    for (int i=1;i<=m;i++)  in(val[i]),r[i]=val[i];
    if (n==m)
    {
        for (int i=1;i<=n;i++)  for (edge *j=prev[i];j;j=j->next)   ans+=abs(r[i]-r[j->to]);
        return printf("%lld\n",ans>>1),0;
    }
    for (int i=1;i<=m;i++)  del[i]=1,q[++tail]=i;
    while (head<=tail)
    {
        int last=tail;
        for (int x=head;x<=tail;x++)    for (edge *i=prev[q[x]];i;i=i->next)    if (!del[i->to])    Insert(i->to,q[x]);
        for (;head<=last;head++)    for (edge *i=prev[q[head]];i;i=i->next)     if (!del[i->to]&&(--d[i->to])<=1)   del[i->to]=1,q[++tail]=i->to;
    }
    for (int i=1;i<=n;i++)  if (!f[i])  Insert(0,i);
    dfs(0);cout<<ans<<endl;
}

你可能感兴趣的:(重构图)