358. 岛屿
题目链接
358. 岛屿
你准备游览一个公园,该公园由 \(N\) 个岛屿组成,当地管理部门从每个岛屿出发向另外一个岛屿建了一座桥,不过桥是可以双向行走的。
同时,每对岛屿之间都有一艘专用的往来两岛之间的渡船。
相对于乘船而言,你更喜欢步行。
你希望所经过的桥的总长度尽可能的长,但受到以下的限制:
- 可以自行挑选一个岛开始游览。
- 任何一个岛都不能游览一次以上。
- 无论任何时间你都可以由你现在所在的岛 \(S\) 去另一个你从未到过的岛 \(D\)。由 \(S\) 到 \(D\) 可以有以下方法:
(1)步行:仅当两个岛之间有一座桥时才有可能。对于这种情况,桥的长度会累加到你步行的总距离中。
(2)渡船:你可以选择这种方法,仅当没有任何桥和以前使用过的渡船的组合可以由 \(S\) 走到 \(D\)(当检查是否可到达时,你应该考虑所有的路径,包括经过你曾游览过的那些岛)。
注意,你不必游览所有的岛,也可能无法走完所有的桥。
请你编写一个程序,给定 \(N\) 座桥以及它们的长度,按照上述的规则,计算你可以走过的桥的最大长度。
输入格式
第 \(1\) 行包含整数 \(N\)。
第 \(2.. N+1\) 行,每行包含两个整数 \(a\) 和 \(L\),第 \(i+1\) 行表示岛屿 \(i\) 上建了一座通向岛屿 \(a\) 的桥,桥的长度为 \(L\)。
输出格式
输出一个整数,表示结果。
对某些测试,答案可能无法放进 \(32-bit\) 整数。
数据范围
\(2 \le N \le 10^6\),
\(1 \le L \le 10^8\)
输入样例:
7
3 8
7 2
4 2
1 4
1 9
3 4
2 3
输出样例:
24
解题思路
基环树dp
题目即求解基环树的直径,分为两部分:经过环和不经过环,不经过环直径说明在基环的某个点为根节点的子树上,这部分即为树的直径,经过环则考虑三部分:\(d[x],d[y]\) (其中 \(d[i]\) 表示环上 \(i\) 为根节点的子树包含 \(i\) 的最长直径)以及环上的部分,对于环上的部分,可以预处理前缀和 \(sum[i]\),默认按照顺时针的顺序,\(x\) 到 \(y\) 的贡献即为 \(d[x]+d[y]+s[y]-s[x]=d[y]+s[y]+d[x]-s[x]\),其中 \(d[y]+s[y]\) 固定,要求在距离环的长度内找 \(d[x]-s[x]\) 的最大值,即转化为滑动窗口问题
- 时间复杂度:\(O(n)\)
代码
// Problem: 岛屿
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/360/
// Memory Limit: 256 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=1e6+5,M=2*N;
int n;
int h[N],e[M],ne[M],w[M],idx;
int cir[N],ed[N],cnt,fu[N],fw[N];
LL s[N],d[M],sum[M],ans;
bool st[N],in[N];
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void dfs_c(int x,int from)
{
st[x]=in[x]=true;
for(int i=h[x];~i;i=ne[i])
{
if(i==(from^1))continue;
int y=e[i];
fu[y]=x,fw[y]=w[i];
if(!st[y])dfs_c(y,i);
else if(in[y])
{
cnt++;
ed[cnt]=ed[cnt-1];
LL sum=w[i];
for(int z=x;z!=y;z=fu[z])
{
s[z]=sum;
sum+=fw[z];
cir[++ed[cnt]]=z;
}
s[y]=sum,cir[++ed[cnt]]=y;
}
}
in[x]=false;
}
LL dfs_d(int x)
{
st[x]=true;
LL d1=0,d2=0;
for(int i=h[x];~i;i=ne[i])
{
int y=e[i];
if(st[y])continue;
LL d=dfs_d(y)+w[i];
if(d>=d1)d2=d1,d1=d;
else if(d>d2)d2=d;
}
ans=max(ans,d1+d2);
return d1;
}
int main()
{
scanf("%d",&n);
memset(h,-1,sizeof h);
for(int i=1;i<=n;i++)
{
int a,l;
scanf("%d%d",&a,&l);
add(i,a,l),add(a,i,l);
}
for(int i=1;i<=n;i++)
if(!st[i])dfs_c(i,-1);
memset(st,0,sizeof st);
for(int i=1;i<=ed[cnt];i++)st[cir[i]]=true;
LL res=0;
for(int i=1;i<=cnt;i++)
{
ans=0;
int sz=0;
for(int j=ed[i-1]+1;j<=ed[i];j++)
d[++sz]=dfs_d(cir[j]),sum[sz]=s[cir[j]];
for(int i=1;i<=sz;i++)d[i+sz]=d[i],sum[i+sz]=sum[i]+sum[sz];
deque<int> q;
for(int j=1;j<=sz*2;j++)
{
while(q.size()&&j-q.front()+1>sz)q.pop_front();
if(q.size())ans=max(ans,d[j]+sum[j]+d[q.front()]-sum[q.front()]);
while(q.size()&&d[q.back()]-sum[q.back()]<=d[j]-sum[j])q.pop_back();
q.pb(j);
}
res+=ans;
}
printf("%lld",res);
return 0;
}