题目链接:
HDU 4126 Genghis Khan the Conqueror
题意:
给
n
个点和
数据范围:
n≤3000,Q≤104,单边权≤107,总边权≤109
分析:
先求一遍最小生成树,设代价为
sum
。如果破坏的边不是最小生成树的边,那么这次的代价就是
sum
。如果破坏的边是最小生成树的边,我们根据这条边的两个端点把这棵树分成两个点集,设将这两个点集连通的最小代价为
tmp
(也就是一个集合中的所有点到另一个集合中的所有点的最小边权),那么这次破坏的后生成树的代价为
sum−dis[u][v]+min(tmp,w)
,其中
u
和
因为破坏的次数
我们记
dp[u][v]
为破坏边
(u,v)
之后以
u
为根的子树和以
我们来考虑每个节点
root
对以其他所有节点为根的所有子树与包含u的子树的
dp[][]
影响。例如当前考虑截断节点
u
和
求最小生成树时如果用
Kruskal
,就是
O(mlogm)
,如果用
Prim
,就是
O(n2)
,所以最终的时间复杂度是:
O(mlogm+n2+Q)
或者
O(2∗n2+Q)
。
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <climits>
using namespace std;
const int MAX_N = 3010;
int n, m, Q, total;
int head[MAX_N], dp[MAX_N][MAX_N], fa[MAX_N], dis[MAX_N][MAX_N];
int used[MAX_N][MAX_N];
struct Edge {
int u, v, w;
bool operator < (const Edge& rhs) const {
return w < rhs.w;
}
} edge[MAX_N * MAX_N];
struct Mst {
int v, next;
} mst[MAX_N * 2];
int find(int x)
{
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void AddEdge(int u, int v)
{
mst[total].v = v;
mst[total].next = head[u];
head[u] = total++;
}
int Kruskal()
{
total = 0;
sort(edge, edge + m);
int res = 0;
for (int i = 0; i < m; ++i) {
int u = edge[i].u, v = edge[i].v, w = edge[i].w;
int fu = find(u), fv = find(v);
if (fu != fv) {
fa[fu] = fv;
res += w;
used[u][v] = used[v][u] = 1;
AddEdge(u, v);
AddEdge(v, u);
}
}
return res;
}
int dfs(int root, int u, int p)
{
int Min = INT_MAX;
for (int i = head[u]; i != -1; i = mst[i].next) {
int v = mst[i].v;
if (v == p) continue;
int tmp = dfs(root, v, u);
Min = min(Min, tmp);
dp[u][v] = dp[v][u] = min(dp[u][v], tmp);
}
if (p != -1 && p != root) Min = min(Min, dis[u][root]);
return Min;
}
int main()
{
while (~scanf("%d%d", &n, &m) && (n || m)) {
for (int i = 0; i <= n; ++i) fa[i] = i;
memset(dis, 0x3f, sizeof(dis));
memset(head, -1, sizeof(head));
memset(used, 0, sizeof(used));
for (int i = 0; i < m; ++i) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
edge[i].u = u, edge[i].v = v, edge[i].w = w;
dis[u][v] = dis[v][u] = w;
}
int sum = Kruskal();
memset(dp, 0x3f, sizeof(dp));
for (int i = 0; i < n; ++i) dfs(i, i, -1);
scanf("%d", &Q);
double ans = 0;
for (int i = 0; i < Q; ++i) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
if (used[u][v]) ans += sum - dis[u][v] + min(dp[u][v], w);
else ans += sum;
}
printf("%.4lf\n", ans / (1.0 * Q));
}
return 0;
}
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <climits>
#include <vector>
using namespace std;
const int MAX_N = 3010;
const int inf = 0x3f3f3f3f;
int n, m, Q;
int dis[MAX_N][MAX_N], used[MAX_N][MAX_N], dp[MAX_N][MAX_N];
int way[MAX_N], vis[MAX_N], fa[MAX_N];
vector<int> vec[MAX_N];
void init()
{
for (int i = 0; i < n; ++i) {
vec[i].clear();
vis[i] = 0;
for (int j = 0; j < n; ++j) {
used[i][j] = 0;
dp[i][j] = dis[i][j] = inf;
}
}
}
int Prim()
{
int res = 0;
vis[0] = 1;
for (int i = 0; i < n; ++i) {
way[i] = dis[i][0];
fa[i] = 0;
}
vis[0] = 1, fa[0] = -1;
for (int i = 1; i < n; ++i) {
int Min = inf, id;
for (int j = 0; j < n; ++j) {
if (!vis[j] && way[j] < Min) {
Min = way[j], id = j;
}
}
vis[id] = 1;
res += Min;
// printf("id = %d fa[id] = %d\n", id, fa[id]);
if (fa[id] != -1) {
vec[id].push_back(fa[id]);
vec[fa[id]].push_back(id);
}
for (int j = 0; j < n; ++j) {
if (!vis[j] && dis[id][j] < way[j]) {
way[j] = dis[id][j];
fa[j] = id;
}
}
}
// printf("res = %d\n", res);
return res;
}
int dfs(int root, int u, int p)
{
int Min = inf;
for (int i = 0; i < vec[u].size(); ++i) {
int v = vec[u][i];
if (v == p) continue;
int tmp = dfs(root, v, u);
Min = min(Min, tmp);
dp[u][v] = dp[v][u] = min(dp[u][v], tmp);
}
if (p != -1 && p != root) Min = min(Min, dis[root][u]);
return Min;
}
int main()
{
while (~scanf("%d%d", &n, &m) && (n || m)) {
init();
for (int i = 0; i < m; ++i) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
dis[u][v] = dis[v][u] = w;
}
int sum = Prim();
for (int i = 0; i < n; ++i) dfs(i, i, -1);
scanf("%d", &Q);
double ans = 0;
for (int i = 0; i < Q; ++i) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
if (fa[u] != v && fa[v] != u) ans += sum;
else ans += sum - dis[u][v] + min(w, dp[u][v]);
// printf("u = %d fa[u] = %d v = %d fa[v] = %d\n", u, fa[u], v, fa[v]);
// printf("i = %d ans = %.0lf\n", i, ans);
}
printf("%.4lf\n", ans / (1.0 * Q));
}
return 0;
}