看到这道题的时候,我第一反应就是次优比例生成树的变形,但是,思路是这样的没错,却又少许不同的地方,我们来讲一下这里的不同点,依旧是要用到pre[]前缀来记录每个节点的前缀,然后判断的是每个边:若删除这条边,用其他边进行补,会需要多少的最小花费边。
于是问题化简为:我们将[L, R]这条边的价值变为x,需要判断的是:(一)若是这条边是最优比例生成树上的边,那么除去这条边之后的连接两个独立的树的最小代价,就是第一棵树去匹配第二棵树,找到最小解;(二)若是这条边不是最优比例生成树上的边,那么好办了,直接不管就是了,加上最优比例生成树上的权值就可以了。
最优比例生成树好建,怎么判断除去某条边后的连接两棵树的最小权重?那么就有点类似于树形DP,不过只是思维的类似,方法上的处理会简单好多。我们先建立这颗最优比例生成树(这永远是先决条件),然后,我们把树上的节点放进链中,就是树上的一个点需要包含所有与它相连的子节点,这个的目的在于处理后面的除去某边的最短距,我这不用vector<>而是用了链式前向星来做一个时间上的优化,其实差不多。然后,最为重要的就是我们处理的方式,我们利用dfs(目前访问节点,目前访问节点的根),其中还要知道一个全局变量就是根节点(出发节点),这样的一个思路来想,若是从根节点出发,我们会走向两边,但是就是不能走到之前的根节点:光说没用,赋上代码讲:
int dfs(int st, int fa)
{
int len = INF;
for(int u=head[st]; u!=-1; u=rode[u].next)
{
int v=rode[u].to;
if(v!=fa) len = min(len, dfs(v, st) );
}
if(st!=rt && !used[st][rt])
{
len = min(len, edge[st][rt]);
}
path[st][fa] = path[fa][st] = min(len, path[fa][st]);
return len;
}
其中我的len定义为旗下的访问边中最小的距离根节点rt的距离,之后,我们去看目前访问节点与根节点的关系,若不是根节点并且不与根节点构成生成树上的边,我们就去优化len的大小,然后最后更新下path[目前访问节点][其的根](path指的就是去这边以后其他边等效该边的最优解)的距离。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define INF 0x3f3f3f3f
#define pi 3.141592653589793
#define e 2.718281828459045
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int maxN=3005;
int N, M, Q, edge[maxN][maxN], path[maxN][maxN], pre[maxN], head[maxN*maxN], cnt, rt;
bool used[maxN][maxN];
struct Eddge
{
int next, to, val;
Eddge(int a=-1, int b=0, int c=0):next(a), to(b), val(c) {}
}rode[maxN*maxN];
void addEddge(int u, int v, int val)
{
rode[cnt] = Eddge(head[u], v, val);
head[u] = cnt++;
}
void init()
{
cnt=0;
for(int i=0; i<N; i++)
{
for(int j=i+1; j<N; j++)
{
edge[i][j] = edge[j][i] = path[i][j] = path[j][i] = INF;
used[i][j] = used[j][i] = false;
}
}
memset(head, -1, sizeof(head));
}
ll prime(int pos)
{
ll ans=0;
bool vis[maxN]; memset(vis, false, sizeof(vis));
int lowercost[maxN];
for(int i=0; i<N; i++) if(i!=pos){ lowercost[i]=edge[pos][i]; pre[i]=pos; }
vis[pos]=true; pre[pos]=-1;
for(int line=1; line<N; line++)
{
int minn=INF;
for(int i=0; i<N; i++)
{
if(!vis[i] && minn>lowercost[i])
{
minn=lowercost[i];
pos=i;
}
}
vis[pos]=true;
addEddge(pos, pre[pos], minn);
addEddge(pre[pos], pos, minn);
used[pos][pre[pos]] = used[pre[pos]][pos] = true;
ans+=minn;
for(int i=0; i<N; i++)
{
if(!vis[i] && lowercost[i]>edge[pos][i]) { lowercost[i]=edge[pos][i]; pre[i]=pos; }
}
}
return ans;
}
int dfs(int st, int fa)
{
int len = INF;
for(int u=head[st]; u!=-1; u=rode[u].next)
{
int v=rode[u].to;
if(v!=fa) len = min(len, dfs(v, st) );
}
if(st!=rt && !used[st][rt])
{
len = min(len, edge[st][rt]);
}
path[st][fa] = path[fa][st] = min(len, path[fa][st]);
return len;
}
void solve()
{
for(int i=0; i<N; i++)
{
rt = i;
dfs(i, i);
}
}
int main()
{
while(scanf("%d%d", &N, &M) && (N | M))
{
init();
for(int i=1; i<=M; i++)
{
int e1, e2, e3;
scanf("%d%d%d", &e1, &e2, &e3);
edge[e1][e2] = edge[e2][e1] = e3;
}
ll Minn_Tree=prime(0), ans=0;
solve();
scanf("%d", &Q);
for(int i=1; i<=Q; i++)
{
int e1, e2, e3;
scanf("%d%d%d", &e1, &e2, &e3);
if(used[e1][e2]) ans += Minn_Tree - edge[e1][e2] + min(e3, path[e1][e2]);
else ans += Minn_Tree;
}
printf("%.4lf\n", (double)ans/(double)Q );
}
return 0;
}