题目链接: E. We Need More Bosses
题目大意:
一个无向图, n个节点, m条边,选择两个节点s和t, 使得从s到t所有路径中都出现的边的数量最大(从s到t无论怎么走都必须要经过这些边)
思路
无向图中,在两点间所有路径出现的边为无向图的桥, 将所有非桥边的权值设为0, 桥的权值设为1(即将边双联通分量缩成一个点),求得出的无向图最长路径,即为答案
桥与割点
定义:
- 无向连通图中,如果某个节点被删除后,图的连通分量数目增加(变为非连通),那么这个点就是关节点(割点)
- 如果某条边删除后,图的连通分量数目增加,那这条边就是桥
算法(白书P312)
- 定义dfn[i]:=dfs遍历时访问i点的次序
ow[i]:=i点或i点后代能通过非父子边能到达的最早的祖先节点 - 为割点的条件:
- i为树根,且有一个以上的子树
- i不是树根,且存在树边(i,son),low[son]>=dfn[i]
- 为桥的条件
树边(i,son), low[son]>dfn[i]
- 定义dfn[i]:=dfs遍历时访问i点的次序
最长路径
任选一点x,求出离x最远的点s,再从s出发,找离s最远的点t,s-t即为最长路径
代码
GNU C++17 Accepted 561 ms 24600 KB
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 100;
int n, m;
struct edge
{
int to, cost;
edge(int to = 0, int cost = 0) {this->to = to; this->cost = cost;}
};
vector<edge> G[maxn];
set<pair<int, int>> Bridges;
int low[maxn], dfn[maxn];
int dfs_clock = 0;
void dfs(int cur, int fa)
{
low[cur] = dfn[cur] = ++dfs_clock;
for (auto &e : G[cur])
{
int son = e.to;
if (!dfn[son])
{
dfs(son, cur);
low[cur] = min(low[cur], low[son]);
if (low[son] > dfn[cur]) Bridges.insert(pair<int, int>(max(cur, son), min(cur, son)));
}
else if(son != fa && dfn[son]<dfn[cur]) low[cur] = min(low[cur], dfn[son]);
}
}
int Max = 0, MaxNode = 0;
int d[maxn];
void dfs1(int cur, int deep)
{
d[cur] = deep;
if (deep > Max) {Max = deep; MaxNode = cur;}
for (auto &e : G[cur])
{
int son = e.to;
if(d[son] == -1)
{
dfs1(son, deep+e.cost);
}
}
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 0; i < m; ++i)
{
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(edge(v, 1));
G[v].push_back(edge(u, 1));
}
dfs(1, -1);
for (int i = 1; i <= n; ++i)
{
for (auto &e : G[i])
{
if (!Bridges.count(pair<int, int>(max(i, e.to), min(i, e.to))) ) e.cost = 0;
}
}
memset(d, -1, sizeof(d));
dfs1(1, 0);
memset(d, -1, sizeof(d));
dfs1(MaxNode, 0);
cout << Max << endl;
return 0;
}