点这里 |
---|
题意: 给定一个带权无向图G,n
个点m
条边,每条边都有一个权值w。题目保证两个点之间最多只有一条边。接下来有Q
次边权的改变,且改变后的权值之后一定比原来的更大。求每次改变边权之后的所有最小生成树的平均值(边权的改变前后不影响。
题解:
- 构造最小生成树: 在没有任何边权改变之前,求出最原始的最小生成树,记录这棵树的权值和
ans
。 - 边权替换: 再次强调,每次边权的改变,是独立操作、前后互补影响的。明确之后再对改变的边权进行分类讨论:
- 边在树上: 这次改变的边在最原始的最小生成树上,并且题目保证改变的边权一定比原来的大,所以一定会影响最小生成树的结果。办法是在原始的最小生成树上,断开这条改变的边,从分开的两棵树,找到一条连接两棵树的最小边。则
新的生成树的权值和 = ans - 改变的边(原来的值) + 连接两棵树的最小边
- 边不在树上: 如果改变的边不在原始的最小生成树上,那其实是不会影响结果的,改变后的最小生成树还是原来的生成树,那么
权值和 = ans
- 边在树上: 这次改变的边在最原始的最小生成树上,并且题目保证改变的边权一定比原来的大,所以一定会影响最小生成树的结果。办法是在原始的最小生成树上,断开这条改变的边,从分开的两棵树,找到一条连接两棵树的最小边。则
- 连接两棵子树的最小边: 既然我们决定将最小生成树上那条改变的边断开,那么这棵树就会分成两棵子树,但是要如何找到连接两棵子树的最小边?
- 树形DP: 定义状态
dp[u][v]
表示点u在的这棵子树到点v在的这颗子树的最小边权。(树形DP这块我解释不下去了,看代码吧)
- 树形DP: 定义状态
过程中犯的错:
- 数据范围: 又一次,我把数组开小了。但是这个可恶的题目告诉我是TLE。害我一直在想办法减小时间复杂度,直到实在优化不了了,才发小数组开小了😢。
- vector E[N]: 用于存放最小生成树上点i连接的所有点,包括子节点和父节点。
#include<bits/stdc++.h>
using namespace std;
const int N = 3e3 + 10;
const int inf = 0x3f3f3f3f;
typedef long long ll;
int n, m, Q, ans;
int u, v, w;
int pre[N];
int G[N][N];
int dp[N][N];
int d[N], vis[N];
vector<int> E[N];
struct edge{
int u, v, w;
bool operator<(const edge &a)const{return a.w < w;}
};
void prim(){
priority_queue<edge> que;
for(int i = 0; i < n; i++) vis[i] = pre[i] = 0, d[i] = G[0][i], E[i].clear(), que.push({0, i, d[i]});
ans = 0, vis[0] = 1, pre[0] = -1, d[0] = inf;
while(!que.empty()){
edge e = que.top(); que.pop();
if(vis[e.v]) continue;
ans += d[e.v];
vis[e.v] = 1;
pre[e.v] = e.u;
E[e.u].push_back(e.v); E[e.v].push_back(e.u);
for(int i = 0; i < n; i++) if(!vis[i] && d[i] > G[e.v][i]) d[i] = G[e.v][i], que.push({e.v, i, d[i]});
}
}
int dfs(int u, int fa, int root){
int res = inf;
for(int i = 0; i < E[u].size(); i++){
int v = E[u][i];
if(v == fa) continue;
int tem = dfs(v, u, root);
res = min(res, tem);
dp[u][v] = dp[v][u] = min(dp[u][v], tem);
}
if(root != fa) res = min(res, G[root][u]);
return res;
}
int main(){
while(~scanf("%d%d", &n, &m) && n && m){
for(int i = 0; i < n; i++) for(int j = 0; j < n; j++) G[i][j] = dp[i][j] = inf;
for(int i = 0; i < m; i++){
scanf("%d%d%d", &u, &v, &w);
G[u][v] = G[v][u] = w;
}
prim();
for(int i = 0; i < n; i++) dfs(i, -1, i);
ll sum = 0;
scanf("%d", &Q);
for(int i = 1; i <= Q; i++){
scanf("%d%d%d", &u, &v, &w);
if(pre[u] != v && pre[v] != u) sum += ans;
else sum += (ans - G[u][v] + min(w, dp[u][v]));
}
printf("%.4lf\n", 1.0 * sum / Q);
}
return 0;
}