358. 岛屿

题目链接

358. 岛屿

你准备游览一个公园,该公园由 N 个岛屿组成,当地管理部门从每个岛屿出发向另外一个岛屿建了一座桥,不过桥是可以双向行走的。

同时,每对岛屿之间都有一艘专用的往来两岛之间的渡船。

相对于乘船而言,你更喜欢步行。

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

  1. 可以自行挑选一个岛开始游览。
  2. 任何一个岛都不能游览一次以上。
  3. 无论任何时间你都可以由你现在所在的岛 S 去另一个你从未到过的岛 D。由 SD 可以有以下方法:
    (1)步行:仅当两个岛之间有一座桥时才有可能。对于这种情况,桥的长度会累加到你步行的总距离中。
    (2)渡船:你可以选择这种方法,仅当没有任何桥和以前使用过的渡船的组合可以由 S 走到 D(当检查是否可到达时,你应该考虑所有的路径,包括经过你曾游览过的那些岛)。

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

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

输入格式

1 行包含整数 N

2..N+1 行,每行包含两个整数 aL,第 i+1 行表示岛屿 i 上建了一座通向岛屿 a 的桥,桥的长度为 L

输出格式

输出一个整数,表示结果。

对某些测试,答案可能无法放进 32bit 整数。

数据范围

2N106,
1L108

输入样例:

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],默认按照顺时针的顺序,xy 的贡献即为 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; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/16786370.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(45)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示