题意:有一个无向图,存在一个最小生成树,然后给q次询问,即把一条边的权值改成一个更大的值,问换一条边之后的最小生成树的权值,然后求每次询问之后最小生成树权值的综合然后除以q。
想法:就是先求出最小生成树,首先我们知道如果删掉一条最小生成树的边,那么此时,最小生成树就会变成两个连通分量,那么我们就用dp[a][b]表示删除边a,b,之后连通分量之间的最短距离。显然从一个点进行dfs,可以遍历到最小生成树的每一条边,那么如果枚举到的这个边,就为此时我们要删除的边,那么树就自动被分成了两个连通分量。但是需要控制一个变量,就是设连通分量A中到B中所有点,距离最短的点为root,进行控制变量。
主要dfs的过程:说一下最重要的地方,就是为什么当root=fa时候不可以更新最短的距离。当删除u和root之间的边的时候,对于最小生成树来说,最短的就是这条边,现在要找的是除此以外,root为A集合控制点,到B集合的最短距离,即次短,如果要是不加这个条件,那么A到B的最短距离永远是root到u的距离,显然违背要删除此边的用意。
#include<iostream> #include<cstdio> #include<cstring> #define inf 0x7fffffff using namespace std; const int nodes=5000+100; const int edges=5100*5100*4; int n,m; struct node { int v,next; }e[edges]; int head[nodes],cnt; double map[nodes][nodes],dis[nodes],mst_sum,dp[nodes][nodes]; int pre[nodes],vis[nodes]; int used[nodes][nodes]; void Init() { memset(head,-1,sizeof(head)); memset(used,0,sizeof(used)); cnt=0; } void add(int a,int b) { e[cnt].v=b; e[cnt].next=head[a]; head[a]=cnt++; } double Min(double a,double b) { if(a<b) return a; return b; } void prime() { mst_sum=0; for(int i=0;i<n;i++) { dis[i]=map[0][i]; pre[i]=0; } memset(vis,0,sizeof(vis)); vis[0]=1; pre[0]=-1; for(int i=1;i<n;i++) { double minn=(double)inf; int pos; for(int j=0;j<n;j++) { if(!vis[j]&&minn>dis[j]) { minn=dis[j]; pos=j; } } vis[pos]=1; used[pre[pos]][pos]=used[pos][pre[pos]]=1; add(pre[pos],pos);add(pos,pre[pos]); mst_sum+=minn; for(int j=0;j<n;j++) { if(!vis[j]&&dis[j]>map[pos][j]) { dis[j]=map[pos][j]; pre[j]=pos; } } } } double dfs(int root,int u,int fa) { double res=(double)inf; for(int i=head[u];i+1;i=e[i].next) { int v=e[i].v; if(v==fa) continue; double tmp=dfs(root,v,u); dp[u][v]=dp[v][u]=Min(dp[u][v],tmp); res=Min(res,tmp); } if(fa!=root) { res=Min(res,map[root][u]); } return res; } int main() { while(~scanf("%d%d",&n,&m),n+m) { Init(); for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { if(i==j) map[i][j]=0; else map[i][j]=inf; dp[i][j]=inf; } } for(int i=1;i<=m;i++) { int a,b; double c; scanf("%d%d%lf",&a,&b,&c); map[a][b]=map[b][a]=c; } prime(); for(int i=0;i<n;i++) { dfs(i,i,-1); } int q; double sum=0; scanf("%d",&q); for(int k=1;k<=q;k++) { int a,b; double c; scanf("%d%d%lf",&a,&b,&c); double res; if(used[a][b]) { if(dp[a][b]<c) { res=mst_sum-map[a][b]+dp[a][b]; } else { res=mst_sum-map[a][b]+c; } } else res=mst_sum; sum+=res; } printf("%.4lf\n",sum/q); } return 0; }