首页 > 其他 > 详细

[IOI2008]Island

时间:2020-02-18 20:09:21      阅读:68      评论:0      收藏:0      [点我收藏+]

[IOI2008]Island(luogu)

Description

题目描述

 

你准备浏览一个公园,该公园由 N 个岛屿组成,当地管理部门从每个岛屿 i 出发向另外一个岛屿建了一座长度为 L_i 的桥,

不过桥是可以双向行走的。同时,每对岛屿之间都有一艘专用的往来两岛之间的渡船。相对于乘船而言,你更喜欢步行。

你希望经过的桥的总长度尽可能长,但受到以下的限制:

  • 可以自行挑选一个岛开始游览。
  • 任何一个岛都不能游览一次以上。
  • 无论任何时间,你都可以由当前所在的岛 SS 去另一个从未到过的岛 DD。从 SS 到 DD 有如下方法:

1.步行:仅当两个岛之间有一座桥时才有可能。对于这种情况,桥的长度会累加到你步行的总距离中。

2.渡船:你可以选择这种方法,仅当没有任何桥和以前使用过的渡船的组合可以由 SS 走到 D

(当检查是否可到达时,你应该考虑所有的路径,包括经过你曾游览过的那些岛)。

 

注意,你不必游览所有的岛,也可能无法走完所有的桥。

请你编写一个程序,给定 NN 座桥以及它们的长度,按照上述的规则,计算你可以走过的桥的长度之和的最大值。

 

输入格式

 

第一行包含 NN 个整数,即公园内岛屿的数目。

随后的 NN 行每一行用来表示一个岛。第 ii 行由两个以单空格分隔的整数,表示由岛 ii 筑的桥。

第一个整数表示桥另一端的岛,第二个整数表示该桥的长度 L_i

你可以假设对于每座桥,其端点总是位于不同的岛上。

 

输出格式

仅包含一个整数,即可能的最大步行距离。

 

Solution

读入

 

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%lld",&x,&y);
        link[i].push_back((node){x,y});
        link[x].push_back((node){i,y});
    }
}

 

 

 

由题意可知当只把桥加入图中时,每个点的出度为 1 ,且一共有N条边,

这不就是外向基环树森林的模型吗。

由要求知,只能在两棵基环树间乘坐渡船(其他情况下可由桥到达),

且乘坐渡船从一棵基环树出去后不能再返回这棵树

于是我们求出每棵基环树的直径并累计

(基环树中最长的简单路径被称为基环树的最长链,其长度被称为基环树的直径。

这里,简单路径指的是不自交(不重复经过任何点或边)的路径)

 


 

基环树直径的求法

  • 找到环并标记,记录边长

 

void dfs(int u,int fx)
{
    flag[u]=true,fa[u]=fx;
    int size=link[u].size();
    int cnt=0;
    for(int i=0;i<size;i++)
    {
        int v=link[u][i].v;
        if((v==fx && ++cnt==1) || cir[v]) continue;
        if(flag[v])
        {
            int x=u;
            while(x!=v)  a[++a[0]]=x,d[++d[0]]=w[x],cir[x]=true,x=fa[x];
            cir[v]=true,a[++a[0]]=v,d[++d[0]]=link[u][i].w;
        }
        else w[v]=link[u][i].w,dfs(v,u);
    }
}

 

  • 找到去掉环后 以环中每个点为根的 不经过环中其他点的 树 的直径和最大深度,用这些树的直径更新基环树直径
void dfs1(int u,int fx,ll res,ll &x)
{
    int size=link[u].size();
    for(int i=0;i<size;i++)
    {
        int v=link[u][i].v;
        if(v==fx || cir[v]) continue;
        dfs1(v,u,res+link[u][i].w,x);
        if(b[u][0]<b[v][0]+link[u][i].w)
        {
            b[u][1]=b[u][0];
            b[u][0]=b[v][0]+link[u][i].w;
        }
        else b[u][1]=max(b[u][1],b[v][0]+link[u][i].w);
    }
    x=max(x,max(res+b[u][0],b[u][0]+b[u][1]));
}
  • 单调队列求经过环的最大直径
    for(int i=1;i<=n;i++)
        if(!flag[i])
        {
            d[0]=0,a[0]=0,dfs(i,0);
            an=0;
            for(int j=1;j<=a[0];j++)
                dfs1(a[j],0,0,an),ma[j]=b[a[j]][0];
            ll cc=d[d[0]];
            for(int j=d[0];j>1;j--) d[j]=d[j-1];
            d[1]=cc;
            d[0]=0;
            for(int j=a[0]+1;j<=a[0]*2;j++)
                ma[j]=ma[j-a[0]],d[j]=d[j-a[0]];
            for(int j=1;j<=a[0]*2;j++) d[j]+=d[j-1];
            l=1,r=0;
            for(int j=1;j<=2*a[0];j++)
            {
                while(l<=r && j-q[l]>=a[0]) l++;
                if(l<=r) an=max(an,ma[q[l]]-d[q[l]]+d[j]+ma[j]);
                while(l<=r && ma[q[r]]-d[q[r]]<=ma[j]-d[j]) r--;
                q[++r]=j;
            }
            ans+=an;
        }

 

Code

#include <cstdio>
#include <cstdlib>
#include <vector>
#include <algorithm>
#define ll long long
using namespace std;
const int N=1e6+10;
bool flag[N],cir[N];
struct node
{
    int v;
    ll w;
};
vector <node> link[N];
int x,n,fa[N],a[N*2],q[N*2],l,r;
ll ans,y,d[N*2],w[N],ma[N*2],b[N][2],an;
void dfs(int u,int fx)
{
    flag[u]=true,fa[u]=fx;
    int size=link[u].size();
    int cnt=0;
    for(int i=0;i<size;i++)
    {
        int v=link[u][i].v;
        if((v==fx && ++cnt==1) || cir[v]) continue;
        if(flag[v])
        {
            int x=u;
            while(x!=v) a[++a[0]]=x,d[++d[0]]=w[x],cir[x]=true,x=fa[x];
            cir[v]=true,a[++a[0]]=v,d[++d[0]]=link[u][i].w;
        }
        else w[v]=link[u][i].w,dfs(v,u);
    }
}
void dfs1(int u,int fx,ll res,ll &x)
{
    int size=link[u].size();
    for(int i=0;i<size;i++)
    {
        int v=link[u][i].v;
        if(v==fx || cir[v]) continue;
        dfs1(v,u,res+link[u][i].w,x);
        if(b[u][0]<b[v][0]+link[u][i].w)
        {
            b[u][1]=b[u][0];
            b[u][0]=b[v][0]+link[u][i].w;
        }
        else b[u][1]=max(b[u][1],b[v][0]+link[u][i].w);
    }
    x=max(x,max(res+b[u][0],b[u][0]+b[u][1]));
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%lld",&x,&y);
        link[i].push_back((node){x,y});
        link[x].push_back((node){i,y});
    }
    for(int i=1;i<=n;i++)
        if(!flag[i])
        {
            d[0]=0,a[0]=0,dfs(i,0);
            an=0;
            for(int j=1;j<=a[0];j++)
                dfs1(a[j],0,0,an),ma[j]=b[a[j]][0];
            ll cc=d[d[0]];
            for(int j=d[0];j>1;j--) d[j]=d[j-1];
            d[1]=cc;
            d[0]=0;
            for(int j=a[0]+1;j<=a[0]*2;j++)
                ma[j]=ma[j-a[0]],d[j]=d[j-a[0]];
            for(int j=1;j<=a[0]*2;j++) d[j]+=d[j-1];
            l=1,r=0;
            for(int j=1;j<=2*a[0];j++)
            {
                while(l<=r && j-q[l]>=a[0]) l++;
                if(l<=r) an=max(an,ma[q[l]]-d[q[l]]+d[j]+ma[j]);
                while(l<=r && ma[q[r]]-d[q[r]]<=ma[j]-d[j]) r--;
                q[++r]=j;
            }
            ans+=an;
        }
    printf("%lld\n",ans);
    return 0;
}

 

void dfs(int u,int fx){flag[u]=true,fa[u]=fx;int size=link[u].size();int cnt=0;for(int i=0;i<size;i++){int v=link[u][i].v;if((v==fx && ++cnt==1) || cir[v]) continue;if(flag[v]){int x=u;while(x!=v) a[++a[0]]=x,d[++d[0]]=w[x],cir[x]=true,x=fa[x];cir[v]=true,a[++a[0]]=v,d[++d[0]]=link[u][i].w;}else w[v]=link[u][i].w,dfs(v,u);}}

[IOI2008]Island

原文:https://www.cnblogs.com/hsez-cyx/p/12327439.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!