题解 【P4381 [IOI2008] Island】

[IOI2008] Island

题意:

给定 \(n\)个点 \(n\)条边,每条边有边权。你可以从任意一个点开始,任意两点间不管有没有边都可以走,若两点间有边,则将边权加入答案,记录总和。其中,每个点只能经过一次。求答案的最大值。

分析:

显然,该图是一个基环树。对于环,我们有两种解决方法,即破环成链和倍长。

接下来,预处理出树的遍历顺序(即时间戳),顺便找到树上的环,并记录下来。首先破环求各个环上节点的子树直径,并记录 每个点在自己的子树中的最长距离(即代码中的 \(d\)数组),然后再将环倍长,通过单调队列优化记录答案。

code:

#include <bits/stdc++.h>
#define int long long
#define N 2000010
using namespace std;

int n, p, ans, cnt, num, answer, d[N], s[N], q[N], fa[N], dfn[N], sum[N], head[N];
bool v[N];

struct xcj{
	int to, nxt, value;
} e[N];

void add(int u, int v, int w){e[++cnt] = {v, head[u], w}, head[u] = cnt;}//链式前向星存图 

void get_cycle(int x, int y, int z){//找环 
	sum[1] = z;
	while (y != x) {
		s[++p] = y;
		sum[p + 1] = e[fa[y]].value;
		y = e[fa[y] ^ 1].to;
	}
	s[++p] = x;
	for (int i = 1; i <= p; i++) {
		v[s[i]] = true;
		s[p + i] = s[i];
		sum[p + i] = sum[i];
	}
	for (int i = 1; i <= 2 * p; i++) sum[i] += sum[i - 1];
}

void dfs1(int x){
	dfn[x] = ++num;//记录时间戳
	for (int i = head[x]; i; i = e[i].nxt){
		int y = e[i].to;
		if (!dfn[y]) fa[y] = i, dfs1(y);
		else if ((i ^ 1) != fa[x] && dfn[y] > dfn[x]) get_cycle(x, y, e[i].value);//判环 
	}
}

void dfs(int x){//dp求树的直径 
	v[x] = 1;
	for (int i = head[x]; i; i = e[i].nxt){
		int y = e[i].to;
		if (v[y]) continue;
		dfs(y), ans = max(ans, d[x] + d[y] + e[i].value), d[x] = max(d[x], d[y] + e[i].value);//更新最大距离 
	}
}

signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin >> n, cnt = 1;//链式前向星从2开始连边可以通过异或来寻找两点之前的无向边
	for (int i = 1, v, w; i <= n; ++i) cin >> v >> w, add(i, v, w), add(v, i, w);
	for (int u = 1; u <= n; ++u)//遍历每个节点
		if (!dfn[u]){
			p = ans = 0;
			dfs1(u);
			for (int i = 1; i <= p; i++) dfs(s[i]);//破环求子树直径 
			int l = 1, r = 0;
			for (int i = 1; i <= 2 * p; i++){//单调队列优化 
				while (l <= r && q[l] <= i - p) l++;
				if (l <= r) ans = max(ans, d[s[i]] + d[s[q[l]]] + sum[i] - sum[q[l]]);
				while (l <= r && d[s[q[r]]] - sum[q[r]] <= d[s[i]] - sum[i]) r--;
				q[++r] = i;
			}
			answer += ans;
		}
	cout << answer << endl;
	return 0;
}
posted @ 2022-02-24 19:49  leoair  阅读(75)  评论(0)    收藏  举报