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
", mx);
dfs(rt);
for (int i = 0; i < m; ++i)
printf("%d %d
", 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
", res[i]);
}
return 0;
}