【NOIP模拟】周年纪念日

题面

WZland 的国王决定举办一个晚会,这次晚会要求所有的 WZland 居民都参 加,但是他发现 WZland 的所有城市之间的道路都已经毁坏(平时 WZland 的居 民都在自己的城市里活动,所以他们对于那些道路一点都不关心)。这是一件十 分麻烦的事情,因为这个晚会要求所有居民都来参加,于是国王决定要重建道路。 他找出了 WZland 的地图,他发现 WZland 有 N 个城市,有 M 条破败的双向 道路连接着这 N 个城市。由于周年纪念日就要到来,为了节省时间国王决定修 建的道路恰好将这 N 个城市连接起来。修建一条长度为 L 的道路,需要花费 L 个 Ws(Ws 是 WZland 的通用货币),国王想要将总的花费降到最少,这样他能有 足够多的钱来举办晚会。国王还有一个要求,因为这些道路是同时开始修建的, 因此修建完这些道路的总时间等于修建完最长的那条道路的时间(你可以认为所 有工人的修建速度是一样的,即一单位时间修建 1 单位长度的道路),国王想要 总的修建时间最少。 由于要举行晚会,国王需要找到一个地点来举办晚会,这同样是一件十分麻 烦的事情。因为 WZland 的每个人都十分懒,他们不愿意多走路(就连在这周年 纪念日也不例外)。 WZland 的居民每走 1 单位长度的路就会产生 1 单位的不高 兴度(这就是为什么他们都不愿离开自己城市的原因)。WZland 的国王想要这个 晚会的不高兴度尽量低(晚会的不高兴度就等于所有参加晚会的人的不高兴度的 和),这就要求选取一个合适的地点来举办晚会(WZland 的居民要通过即将修建 好的道路,来到这个晚会举办地)。 举个例子,如果晚会在城市 u 举行,城市 i 有 Pi 个人,城市 u 和城市 i 的距 离为 D(u,i),那么这些人产生的不高兴度之和为 Pi*D(u,i)。 国王把这个任务交给了你,他希望你能找出一个地点,是所有人的不高兴度 之和最小。

 

分析

显然第一个子问题是最小生成树

关键在于第二个问题如何处理

第一次看到的时候会想到树的中心之类的,但是它多了一个每个点的点权的限制,就很困难了。

因为最后已经是一棵树了,所以会想到树形dp

如果是从儿子转移到父亲,那就要以每个点为根dfs,才将子节点到根的不高兴度全部处理出来,并且才能比较出最小的,很显然这是30分做法

所以考虑从父亲转移到儿子,假设已经处理了以当前节点v的父亲节点u为根的不高兴度dp[u],显然,当前节点到父亲节点,以及以当前节点为根的子树中的所有节点的不高兴度都会少(p为点权)p[ ]*dis[u,v]。因为这些节点全部都不用去u节点了,只需要去v节点了,所以不用再通过这条路。但是相应的,本来除了v的子树的其他节点都是不用通过e(u,v)的,现在为了到v,必须从u通过这条路,则除了以v为子树的节点,每个节点都要产生p[ ]*dis[u,v]的不高兴度。

所以 dp[v]=dp[u]-siz[v]*dis[u,v]+(total-siz[v])*dis[u,v]。

化简一下 dp[v]=dp[u]+(total-2*siz[v])*dis[u,v]。

因为是一棵生成树,就随便以一个点为根,预处理一下子树大小,预处理一下那个点的不高兴度,再从那个点开始深搜实现dp

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
using  namespace std;
#define N 100100
#define ll long long
#define INF 0x7fffffff7fffffff
ll first[N],p[N],fa[N],siz[N],dp[N],pre[N],dis[N];
ll n,m,t,cnt,cot,num,len,tot,ans,ansnum;
struct email
{
    ll u,v,w;
    ll nxt;
}e[N*10],g[N*5];

inline void rebuild(ll u,ll v,ll w)
{
    e[++cnt].nxt=first[u];first[u]=cnt;
    e[cnt].v=v;e[cnt].u=u;e[cnt].w=w;
}

inline void add(ll u,ll v,ll w)
{
    g[++cot].v=v;g[cot].u=u;g[cot].w=w;
}

inline ll find(ll x)
{
    return fa[x]==x?x:fa[x]=find(fa[x]);
}

bool cmp(email a,email b)
{
    return a.w<b.w;
}
inline void read(ll &x)
{
    x=0;ll f=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    x=x*f;
}

void kruskal()
{
    for(ll i=1;i<=n;i++)fa[i]=i;
    sort(g+1,g+1+m,cmp);
    for(ll i=1;i<=m;i++)
    {
        ll u=g[i].u,v=g[i].v,w=g[i].w;
        ll fax=find(u),fay=find(v);
        if(fax!=fay)
        {
            fa[fax]=fay;
            rebuild(u,v,w);rebuild(v,u,w);
            len+=w;num++;
        }
        if(num==n-1){t=w;break;}
    }
}

void dfs(ll u,ll f)
{
    for(ll i=first[u];i;i=e[i].nxt)
    {
        ll v=e[i].v,w=e[i].w;
        if(v==f)continue;
        dfs(v,u);
        siz[u]+=siz[v];
        dis[v]=w;
        pre[u]+=pre[v]+siz[v]*dis[v];
    }
}

void DP(ll u,ll f)
{
    if(u!=1)
    dp[u]=dp[f]+dis[u]*(tot-2*siz[u]);
    for(ll i=first[u];i;i=e[i].nxt)
    {
        ll v=e[i].v,w=e[i].w;
        if(v==f)continue;
        DP(v,u);
    }
}

int main()
{
    freopen("anniversary.in","r",stdin);
    freopen("anniversary.out","w",stdout);
    read(n);read(m);
    for(ll i=1;i<=n;i++)
        read(p[i]),tot+=p[i],siz[i]=p[i];
    for(ll i=1;i<=m;i++)
    {
        ll u,v,w;
        read(u);read(v);read(w);
        add(u,v,w);
    }
    kruskal();
    printf("%lld %lld\n",len,t);
    dfs(1,-1);
    dp[1]=pre[1];DP(1,-1);
    ans=INF;
    for(ll i=1;i<=n;i++)
        if(dp[i]<ans)
            ans=dp[i],ansnum=i;
    printf("%lld %lld\n",ansnum,ans);
    return 0;
}

 

转载于:https://www.cnblogs.com/NSD-email0820/p/9570193.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值