洛谷 P4408 逃学的小孩 解题报告

P4408 [NOI2003]逃学的小孩

题目描述

Chris家的电话铃响起了,里面传出了Chris的老师焦急的声音:“喂,是Chris的家长吗?你们的孩子又没来上课,不想参加考试了吗?”一听说要考试,Chris的父母就心急如焚,他们决定在尽量短的时间内找到Chris。他们告诉Chris的老师:“根据以往的经验,Chris现在必然躲在朋友Shermie或Yashiro家里偷玩《拳皇》游戏。现在,我们就从家出发去找Chris,一但找到,我们立刻给您打电话。”说完砰的一声把电话挂了。

Chris居住的城市由N个居住点和若干条连接居住点的双向街道组成,经过街道x需花费Tx分钟。可以保证,任两个居住点间有且仅有一条通路。Chris家在点C,Shermie和Yashiro分别住在点A和点B。Chris的老师和Chris的父母都有城市地图,但Chris的父母知道点A、B、C的具体位置而Chris的老师不知。

为了尽快找到Chris,Chris的父母会遵守以下两条规则:

如果A距离C比B距离C近,那么Chris的父母先去Shermie家寻找Chris,如果找不到,Chris的父母再去Yashiro家;反之亦然。
Chris的父母总沿着两点间唯一的通路行走。
显然,Chris的老师知道Chris的父母在寻找Chris的过程中会遵守以上两条规则,但由于他并不知道A,B,C的具体位置,所以现在他希望你告诉他,最坏情况下Chris的父母要耗费多长时间才能找到Chris?

输入输出格式

输入格式:

输入文件第一行是两个整数\(N(3 ≤ N ≤ 200000)\)\(M\),分别表示居住点总数和街道总数。

以下\(M\)行,每行给出一条街道的信息。第\(i+1\)行包含整数\(U_i\)\(V_i\)\(T_i\) \((1≤U_i, V_i ≤ N,1 ≤ T_i ≤ 1000000000)\),表示街道i连接居住点\(U_i\)\(V_i\),并且经过街道\(i\)需花费\(T_i\)分钟。街道信息不会重复给出。

输出格式:

输出文件仅包含整数T,即最坏情况下Chris的父母需要花费\(T\)分钟才能找到Chris。


我仍未具有将问题很好抽象出模型的能力,所以哪怕大家都说水,我也想到了利用树的直径来贪心的时候,我依旧写爆了这个题。

\(\lmoustache E(C,A)+E(A,B),A,B,C \in G,E(C,A)<E(C,B)\)
\(\rmoustache E(C,B)+E(B,A),A,B,C \in G,E(C,A)>E(C,B)\)

模型即为求解以上的最大值。

我们关注两条链如何分别取到最大值。

扫描二维码关注公众号,回复: 1010589 查看本文章

对于\(E_1(C,A)\)\(E_2(C,B)\),我们发现,对任意点\(C\)\(E_1\),\(E_2\)一定是距离它最远和次远的两个点。

这是什么,就是树的直径的两个端点啊。(求解树的直径以及这是为什么可以看看NOIP2007”树网的核”或者SDOI2011”消防”)

此时另一条链恰好不就是取到了自己的最大值?

那么枚举直径外每个点就好了。


code(代码真的丑QAQ·):

#include <cstdio>
#include <cstring>
#define ll long long
const int N=200010;
ll max(ll x,ll y) {return x>y?x:y;}
ll min(ll x,ll y) {return x<y?x:y;}
struct Edge
{
    ll next,to,w;
}edge[N*2];

ll read()
{
    ll x=0;char c=getchar();
    while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
    return x;
}

int head[N],cnt=0;
void add(ll u,ll v,ll w)
{
    edge[++cnt].next=head[u];edge[cnt].to=v;edge[cnt].w=w;head[u]=cnt;
}
ll n,m;
int used[N];
int l,r;
ll m_max=0;
void f_dfs(ll now,ll ww)
{
    used[now]=1;
    if(m_max<ww)
    {
        m_max=ww;
        l=now;
    }
    for(int i=head[now];i;i=edge[i].next)
    {
        ll v=edge[i].to,w=edge[i].w;
        if(!used[v])
            f_dfs(v,w+ww);
    }
}

ll ge_dfs(ll now)
{
    used[now]=1;
    ll m_max=0;
    for(int i=head[now];i;i=edge[i].next)
    {
        ll v=edge[i].to,w=edge[i].w;
        if(!used[v])
            m_max=max(ge_dfs(v)+w,m_max);
    }
    return m_max;
}

int flag=1;
ll f[N],w0[N],f0[N];
void b_dfs(ll now)
{
    used[now]=1;
    if(now==r)
        flag=0;
    for(int i=head[now];i;i=edge[i].next)
    {
        ll v=edge[i].to,w=edge[i].w;
        if(!used[v]&&flag)
        {
            b_dfs(v);
            if(!flag)
            {
                f[v]=now;
                w0[v]=w;
            }
        }
    }
}
ll ans=0;
ll g[N],cnt0=0;
int main()
{
    n=read(),m=read();
    ll u,v,w;
    for(int i=1;i<=m;i++)
    {
        u=read(),v=read(),w=read();
        add(u,v,w);
        add(v,u,w);
    }
    f_dfs(1,0);//求端点
    r=l;
    m_max=0;
    memset(used,0,sizeof(used));
    f_dfs(r,0);//求端点
    memset(used,0,sizeof(used));
    b_dfs(l);//构建链
    memset(used,0,sizeof(used));
    int now=r;
    while(now)
    {
        used[now]=1;
        g[++cnt0]=now;
        now=f[now];
    }
    for(int i=1;i<=cnt0;i++)
        f0[i]=f0[i-1]+w0[g[i-1]];
    for(int i=1;i<=cnt0;i++)
        ans=max(ans,ge_dfs(g[i])+min(f0[i],f0[cnt0]-f0[i]));
    printf("%lld\n",ans+f0[cnt0]);
    return 0;
}

2018.5.24

猜你喜欢

转载自www.cnblogs.com/ppprseter/p/9083162.html