链接
题意
一个N个点的无向图,先生成一棵最小生成树,然后给你Q次询问,每次询问都是x,y,z的形式, 表示的意思是在原图中将x,y之间的边增大(一定是变大的)到z时,此时最小生成数的值是多少。最后求Q次询问最小生成树的平均值。 N<=3000 , Q<=10000
解析
先用prim算法,求得最小生成树的权值,并且得到最小生成树的图(pre数组的记录他的前驱节点),若加的边在最小生成树外,不必考虑,结果就是最小生成树的权值;若在最小生成树上,就要把以前的边删除,形成两个子树,其子树之间的最小距离,就是最小结果。但是由于k的次数比较多,所以要先求出任意两个连通块的最小距离。其中只能用树形dp,来解决此问题。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn = 3000+100;
const int maxm = maxn*maxn;
int map[maxn][maxn];
int best[maxn][maxn];
int dp[maxn][maxn];
int vis[maxn];
vector<int>g[maxn];
int n, m;
int pre[maxn];
typedef long long LL;
void init() {
memset(dp, inf, sizeof(dp));
memset(best, 0, sizeof(best));
memset(vis, 0, sizeof(vis));
memset(map, inf, sizeof(map));
for (int i=0; i<maxn; i++)
g[i].clear(), pre[i] = -1;
}
int prim() {
int dis[maxn];
int Dis=0;
for (int i=0; i<n; i++) {
dis[i] = map[0][i];
pre[i] = 0;
}
pre[0] = -1;
vis[0] = 1;
dis[0] = inf;
for (int i=0; i<n; i++) {
int minn = inf, k = -1;
for (int j=0; j<n; j++) {
if (dis[j] < minn && !vis[j]) {
minn = dis[j];
k = j;
}
}
if (k == -1)
break;
vis[k] = 1;
Dis += dis[k];
if (pre[k] != -1)
g[k].push_back(pre[k]),
g[pre[k]].push_back(k);
for (int j=0; j<n; j++)
if (!vis[j] && dis[j] > map[k][j])
dis[j] = map[k][j], pre[j] = k;
}
return Dis;
}
int dfs(int u, int p, int rt) { //树形dp
int ans = inf;
for (int i=0; i<g[u].size(); i++) {
int v = g[u][i];
if (v == p)
continue;
int t = dfs(v, u, rt); //找到以v为根子树的连通块的最小距离
ans = min(t, ans);
dp[u][v] = dp[v][u] = min(dp[u][v], t); //记录以u为根和以v为根连通块的最小距离
}
if (rt != p) 找到的边不能是最小生成树的边。
ans = min(ans, map[rt][u]);
return ans;
}
void solve() {
for (int i=0; i<n; i++)
dfs(i, -1, i);
}
int main() {
while (~scanf("%d%d", &n, &m)) {
init();
if (n == 0 && m == 0)
break;
int Dis;
for (int i=0; i<m; i++) {
int u, v, c;
scanf("%d%d%d", &u, &v, &c);
map[u][v] = map[v][u] = c;
}
Dis = prim();
for (int i=0; i<n; i++)
printf("%d ", pre[i]);
cout<<endl;
solve();
int K;
scanf("%d", &K);
double sum = 0;
for (int i=0 ;i<K; i++) {
int u, v, c;
scanf("%d%d%d", &u, &v, &c);
if (pre[u] != v && pre[v] != u)
sum += Dis*1.0;
else
sum += (Dis-map[u][v]+min(dp[u][v], c))*1.0;
}
printf("%.4lf", sum/(K*1.0));
}
return 0;
}