你准备游览一个公园,该公园由 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≤N≤106, 1≤L≤108
输入样例:
73 87 24 21 41 93 42 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_pairusingnamespace std;
typedeflonglong LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> boolchkMax(T &x, T y){ return (y > x) ? x = y, 1 : 0; }
template <typename T> boolchkMin(T &x, T y){ return (y < x) ? x = y, 1 : 0; }
template <typename T> voidinlineread(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;
}
constint 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];
voidadd(int a,int b,int c){
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
voiddfs_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);
elseif(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;
elseif(d>d2)d2=d;
}
ans=max(ans,d1+d2);
return d1;
}
intmain(){
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);
return0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!