【2021noip模拟赛day5】A. 旅游计划

【题意】

求基环树中删去一条边得到的生成树中的直径最小值

【分析】

显然,删去的一定是环上的边才能得到生成树

然后,我们把环拆成链,得到$a_1,a_2,...,a_k$

求出以$a_i$为根的树中的最大深度$dep_i$

然后考虑生成树的直径有可能有以下几种情况:

1.最大深度在某个树中,直接跑一次树的直径

2.考虑删掉$a_i$和$a_{i+1}$之间的边,此时的直径有以下可能:

      ①为1-i之间的路径 ②i+1到k之间的路径 ③1-i和i+1-k之间的路径

我们可以通过预处理出1-i的距离前缀和和i-k的距离后缀和来快速计算上述值

然后直径是三种情况的max,我们要所有这些max中的min即可

 

【代码】

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
const ll inf=1e17;
int head[maxn],tot;
struct edge
{
    int to,nxt;
    ll v;
}e[maxn<<1];
int n;
void add(int x,int y,ll z)
{
    e[++tot].to=y; e[tot].nxt=head[x]; e[tot].v=z; head[x]=tot;
}
int du[maxn],vis[maxn],cir[maxn],top;
void get_circle(int u)
{
    vis[u]=1; cir[++top]=u;
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(du[to]<=1 || vis[to]) continue;
        get_circle(to);
    }
}
ll dis[maxn],maxx,pt;
void dfs1(int u,int fa)
{
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(to==fa || vis[to]) continue;
        dis[to]=dis[u]+e[i].v;
        if(dis[to]>maxx) maxx=dis[to],pt=to;
        dfs1(to,u);
    }
}
ll dist[maxn],ans,dep[maxn];
void dfs2(int u,int fa,int root)
{
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(to==fa || (vis[to] && to!=root)) continue;
        dist[to]=dist[u]+e[i].v;
        maxx=max(maxx,dist[to]);
        dfs2(to,u,root);
    }
}
ll pre_dis[maxn],suf_dis[maxn];
ll pre_ans[maxn],suf_ans[maxn];
ll pre_tmp[maxn],suf_tmp[maxn];
int main()
{
    freopen("travel.in","r",stdin);
    freopen("travel.out","w",stdout);
    scanf("%d",&n);
    int x,y; ll z;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%lld",&x,&y,&z);
        add(x,y,z); add(y,x,z);
        du[x]++; du[y]++;
    }
    queue <int> q;
    for(int i=1;i<=n;i++)
        if(du[i]==1) q.push(i);
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        for(int i=head[u];i;i=e[i].nxt)
        {
            int to=e[i].to; du[to]--;
            if(du[to]==1) q.push(to);
        }
    }
    // for(int i=1;i<=n;i++) printf("%d ",du[i]);
    for(int i=1;i<=n;i++)
        if(du[i]>1)
        {
            get_circle(i);
            break;
        }
    for(int i=1;i<=top;i++)
    {
        int rt=cir[i];
        maxx=0;
        dfs1(rt,0);
        if(maxx) dep[i]=dis[pt],maxx=0,dfs2(pt,0,rt),ans=max(ans,maxx);
    }
/*    for(int i=1;i<=top;i++)
    {
        printf("%d %lld\n",cir[i],dep[i]);
    }*/
    cir[0]=cir[top];
    for(int i=1;i<=top;i++)
        for(int j=head[cir[i]];j;j=e[j].nxt)
        {
            int to=e[j].to;
            if(to==cir[i-1]) {pre_dis[i]=pre_dis[i-1]+e[j].v; break;}
        }
    for(int i=top-1;i>=0;i--)
        for(int j=head[cir[i]];j;j=e[j].nxt)
        {
            int to=e[j].to;
            if(to==cir[i+1]) {suf_dis[i]=suf_dis[i+1]+e[j].v; break;}
        }
    // for(int i=1;i<=top;i++)
    //     printf("%lld\n",dep[i]);
    maxx=-inf;
    for(int i=1;i<=top;i++)
    {
        if(maxx>-inf) pre_ans[i]=max(pre_ans[i-1],pre_dis[i]+dep[i]+maxx);
        maxx=max(maxx,dep[i]-pre_dis[i]);
        pre_tmp[i]=max(pre_tmp[i-1],dep[i]+pre_dis[i]);
    }
    suf_tmp[top]=suf_ans[top]=dep[top]; maxx=-inf;
    for(int i=top;i>=0;i--)
    {
        if(maxx>-inf) suf_ans[i]=max(suf_ans[i+1],suf_dis[i]+dep[i]+maxx);
        maxx=max(maxx,dep[i]-suf_dis[i]);
        suf_tmp[i]=max(suf_tmp[i+1],dep[i]+suf_dis[i]);
    }
    // for(int i=1;i<=top;i++)
    //     printf("%lld %lld %lld %lld\n",pre_ans[i],pre_tmp[i],suf_ans[i],suf_tmp[i]);
    ll res=inf;
    for(int i=2;i<=top;i++)
        res=min(res,max(max(pre_ans[i-1],suf_ans[i]),pre_tmp[i-1]+suf_tmp[i]));
    printf("%lld",max(res,ans));
    return 0;
}

 

开始求直径的时候的第二次dfs,把根节点给拦住了,所以直径求错了!!!

posted @ 2021-10-10 16:30  andyc_03  阅读(30)  评论(0编辑  收藏  举报