358. 岛屿

题目链接

358. 岛屿

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

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

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

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

  1. 可以自行挑选一个岛开始游览。
  2. 任何一个岛都不能游览一次以上。
  3. 无论任何时间你都可以由你现在所在的岛 \(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;
}
posted @ 2022-10-12 22:31  zyy2001  阅读(45)  评论(0编辑  收藏  举报