F. Tourist Reform(2400)(无向图的边双连通分量)

分析:最大的连通分量大小是答案,证明在[https://codeforces.com/blog/entry/47890]。
然后把边的双连通分量大小改成强连通分量。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>

using namespace std;
const int N = 400005;
const int M = 2 * N;
int h[N], e[M], ne[M], idx;
void add(int a, int b)
{
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
	//cout << a << "--" << b << "--" << idx - 1 << endl;
}
bool is_bridge[M];
int dfn[N], low[N], timestamp;
int stk[N], top;
int dcc_cnt;
int id[N];
bool st[N];
int au[M], av[M];
int mx, rt;
void tarjan(int u, int from)
{
	dfn[u] = low[u] = ++timestamp;
	stk[++top] = u;

	for (int i = h[u]; i != -1; i = ne[i])
	{
		int j = e[i];
		if (!dfn[j])
		{
			tarjan(j, i);
			low[u] = min(low[u], low[j]);
			if (dfn[u] < low[j])
				is_bridge[i] = is_bridge[i ^ 1] = true;
		}
		else if (i != (from ^ 1))
		{
			low[u] = min(low[u], dfn[j]);
		}
	}
	if (dfn[u] == low[u])
	{
		int sz = 0;
		++dcc_cnt;
		int y;
		do {
			y = stk[top--];
			id[y] = dcc_cnt;
			++sz;
		} while (y != u);
		if (sz > mx) mx = sz, rt = u;
	}
	
}

void dfs(int u)
{
	st[u] = true;
	for (int i = h[u]; i != -1; i = ne[i])
	{
		int j = e[i];
		if (st[j])
		{
			au[i >> 1] = u;
			av[i >> 1] = j;
			continue;
		}
		if (low[j] != low[u])
		{
			au[i >> 1] = j;
			av[i >> 1] = u;
		}
		else
		{
			au[i >> 1] = u;
			av[i >> 1] = j;
		}
		dfs(j);
	}
}

int main()
{
	int n, m;
	scanf("%d%d", &n, &m);

	memset(h, -1, sizeof h);
	int a, b;
	for (int i = 1; i <= m; ++i)
	{
		scanf("%d%d", &a, &b);
		add(a, b), add(b, a);
	}

	tarjan(1, -1);
	
	/*int mx = 0;*/
	/*for (int i = 1; i <= dcc_cnt; ++i)
	{
		mx = max(mx, sz[i]);
		pos = i;
	}*/

	//最大连通分量的大小
	printf("%d\n", mx);
	
	dfs(rt);
	for (int i = 0; i < m; ++i)
		printf("%d %d\n", au[i], av[i]);
	
	return 0;
}

F. The Shortest Statement (2200)(LCA + Dijkstra + 并查集)

分析:这题细节居多,首先需要用和求最小生成树一样的算法kruskal生成一棵树,先不要把非树边加进图中,然后跑LCA的预处理函数,然后再把非树边加进图中,
然后求这些非树边上的点到每个点的距离,然后再和LCA一起更新答案。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>

using namespace std;
using LL = long long;
using PII = pair<int, int>;
using PLL = pair<LL, LL>;
const LL INF64 = 1e18;
const int N = 100005;
const int M = 200005;
int n, m;
int p[N];
struct Edge
{
	int a, b;
	LL c;
	bool use;//标记为树边还是非树边
};
Edge edges[M];
Edge edges2[50];//非树边
int find(int x)
{
	if (p[x] != x) p[x] = find(p[x]);
	return p[x];
}
int h[N], e[M], ne[M], idx;
LL w[M];
LL dist[100][N];//非树边上的顶点到每个点的距离
vector<int> vv;
int q[N];
int fa[N][19], depth[N];
LL d[N];
bool st[N];
LL res[N];//答案
void add(int a, int b, int c)
{
	e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u, int father)
{
	fa[u][0] = father;
	depth[u] = depth[father] + 1;
	for (int i = 1; i <= 18; ++i) fa[u][i] = fa[fa[u][i - 1]][i - 1];
	for (int i = h[u]; i != -1; i = ne[i])
	{
		int j = e[i];		
		if (j == father) continue;
		d[j] = d[u] + w[i];
		dfs(j, u);
	}
}
int lca(int a, int b)
{
	if (depth[a] < depth[b])
		swap(a, b);

	for (int k = 18; k >= 0; --k)
	{
		if (depth[fa[a][k]] >= depth[b])
		{
			a = fa[a][k];
		}
	}
	if (a == b) return a;
	for (int k = 18; k >= 0; --k)
	{
		if (fa[a][k] != fa[b][k])
		{
			a = fa[a][k];
			b = fa[b][k];
		}
	}
	return fa[a][0];
}
void dijkstra(int s)
{
	for (int i = 1; i <= n; ++i) dist[s][i] = INF64;
	memset(st, 0, sizeof st);
	dist[s][vv[s]] = 0;
	priority_queue<PLL, vector<PLL>, greater<PLL>> heap;
	heap.push({ 0, vv[s] });

	while (heap.size())
	{
		auto t = heap.top();
		heap.pop();
		int ver = t.second;
		LL distance = t.first;
		if (st[ver]) continue;
		for (int i = h[ver]; i != -1; i = ne[i])
		{
			int j = e[i];
			if (dist[s][j] > distance + w[i])
			{
				dist[s][j] = distance + w[i];
				heap.push({ dist[s][j], j });
			}
		}
	}
}
LL distance(int a, int b, int p)
{
	return d[a] + d[b] - 2 * d[p];
}
int main()
{
	
	scanf("%d%d", &n, &m);

	memset(h, -1, sizeof h);
	int a, b, c;
	for (int i = 1; i <= m; ++i)
	{
		scanf("%d%d%lld", &a, &b, &c);
		//add(a, b, c), add(b, a, c);
		edges[i] = { a, b, c };
	}
	for (int i = 1; i <= n; ++i) p[i] = i;
	int cnt = 0;
	for (int i = 1; i <= m; ++i)
	{
		int a = edges[i].a, b = edges[i].b;
		a = find(a), b = find(b);
		
		if (a != b)
		{
			p[a] = b;
			edges[i].use = true;//树边
			++cnt;
		}
		if (cnt == n - 1) break;
	}

	cnt = 0;//非树边数量
	for (int i = 1; i <= m; ++i)
	{
		int a = edges[i].a, b = edges[i].b;
		LL c = edges[i].c;
		if (!edges[i].use)
		{			
			edges2[++cnt] = { a, b, c };
			vv.push_back(a), vv.push_back(b);
		}
		else
		{
			add(a, b, c), add(b, a, c);//树边
		}
	}

	sort(vv.begin(), vv.end());
	vv.erase(unique(vv.begin(), vv.end()), vv.end());

	dfs(1, 0);

	for (int i = 1; i <= cnt; ++i)
	{
		int a = edges2[i].a, b = edges2[i].b;
		LL c = edges2[i].c;
		add(a, b, c), add(b, a, c);
	}

	int q;
	scanf("%d", &q);

	//memset(dist, 0x3f, sizeof dist);
	for (int i = 0; i < vv.size(); ++i)
	{
		dijkstra(i);//映射值
	}

	int u, v;
	for (int i = 1; i <= q; ++i)
	{
		scanf("%d%d", &u, &v);
		res[i] = distance(u, v, lca(u, v));
		for (int j = 0; j < vv.size(); ++j)
		{
			res[i] = min(res[i], dist[j][u] + dist[j][v]);
		}
	}

	for (int i = 1; i <= q; ++i)
	{
		printf("%lld\n", res[i]);
	}

	return 0;
}