LG P4381 [IOI2008]Island

Description

你准备浏览一个公园,该公园由 $N$ 个岛屿组成,当地管理部门从每个岛屿 $i$ 出发向另外一个岛屿建了一座长度为$L_i$ 的桥,不过桥是可以双向行走的。同时,每对岛屿之间都有一艘专用的往来两岛之间的渡船。相对于乘船而言,你更喜欢步行。你希望经过的桥的总长度尽可能长,但受到以下的限制:

    • 可以自行挑选一个岛开始游览。
    • 任何一个岛都不能游览一次以上。
    • 无论任何时间,你都可以由当前所在的岛 $S$

 去另一个从未到过的岛 $D$。从 $S$ 到 $D$ 有如下方法:

    • 步行:仅当两个岛之间有一座桥时才有可能。对于这种情况,桥的长度会累加到你步行的总距离中。
    • 渡船:你可以选择这种方法,仅当没有任何桥和以前使用过的渡船的组合可以由 $S$ 走到 $D$ (当检查是否可到达时,你应该考虑所有的路径,包括经过你曾游览过的那些岛)。

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

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

Solution

求基环森林中每个基环树的直径之和

有两种情况

  • 直径在某个树上(不跨越环)
  • 直径跨越环

对于第一种情况,树形DP

对于第二种情况,优先队列优化DP

#include<iostream>
#include<cstring>
#include<cstdio>
#include<deque>
#include<cmath>
using namespace std;
long long n,tot,head[1000005],vst[1000005],r[1000005],cnt,st;
long long sum[2000005],maxx,dp[2000005],ans,f[2000005];
bool vis[1000005];
struct Edge
{
    long long to,nxt;
    long long w;
}edge[2000005];
inline long long read()
{
    long long w=0,f=1;
    char ch=0;
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        w=(w<<1)+(w<<3)+ch-'0';
        ch=getchar();
    }
    return w*f;
}
bool dfs1(long long k,long long e)
{
    if(vst[k]==1)
    {
        vst[k]=2;
        r[++cnt]=k;
        vis[k]=true;
        return true;
    }
    vst[k]=1;
    for(long long i=head[k];i;i=edge[i].nxt)
    {
        if(i!=((e-1)^1)+1&&dfs1(edge[i].to,i))
        {
            if(vst[k]==1)
            {
                r[++cnt]=k;
                sum[cnt]=sum[cnt-1]+edge[i].w;
                vis[k]=true;
                return true;
            }
            sum[st-1]=sum[st]-edge[i].w;
            return false;
        }
    }
    return false;
}
void dfs2(long long k,long long f)
{
    vis[k]=true;
    for(long long i=head[k];i;i=edge[i].nxt)
    {
        long long v=edge[i].to;
        if(!vis[v])
        {
            dfs2(v,k);
            maxx=max(maxx,dp[k]+dp[v]+edge[i].w);
            dp[k]=max(dp[k],dp[v]+edge[i].w);
        }
    }
}
int main()
{
    n=read();
    for(long long i=1;i<=n;i++)
    {
        long long x=read(),y=read();
        edge[++tot]=(Edge){x,head[i],y};
        head[i]=tot;
        edge[++tot]=(Edge){i,head[x],y};
        head[x]=tot;
    }
    for(long long i=1;i<=n;i++)
    {
        if(!vis[i])
        {
            st=cnt+1;
            maxx=-1;
            dfs1(i,0);
            for(long long j=st;j<=cnt;j++)
            {
                dfs2(r[j],0);
            }
            for(long long j=st;j<=cnt;j++)
            {
                sum[j+cnt-st+1]=sum[j+cnt-st]+sum[j]-sum[j-1];
                f[j]=f[j+cnt-st+1]=dp[r[j]];
            }
            deque<long long>q;
            for(long long j=st;j<=2*cnt-st+1;j++)
            {
                while(q.size()&&q.front()<=j-cnt+st-1)
                {
                    q.pop_front();
                }
                if(q.size())
                {
                    maxx=max(maxx,f[j]+sum[j]+f[q.front()]-sum[q.front()]);
                }
                while(q.size()&&f[j]-sum[j]>=f[q.back()]-sum[q.back()])
                {
                    q.pop_back();
                }
                q.push_back(j);
            }
            ans+=maxx;
        }
    }
    printf("%lld
",ans);
    return 0;
}
Island