题意:
一个N个点的无向图,先生成一棵最小生成树,然后给你Q次询问,每次询问都是x,y,z的形式, 表示的意思是在原图中将x,y之间的边增大(一定是变大的)到z时,此时最小生成数的值是多少。最后求Q次询问最小生成树的平均值。 N<=3000 , Q<=10000
分析:
先用kruskal算法找到最小生成树,并求出总花费sum.再以枚举n个点,依次作为树根dfs,dp[i][j]表示<i,j>为最小生成树上的边,且去掉该边后,包括点i的连通块中的点集A到包括点j的连通块点集B的最小距离。对于根节点为ro,边为<i,j>的dp[i][j]=min(以j节点为根的子树到ro的最短距离,dp[i][j]).也就是每个点均作为根节点枚举每个点到根节点的距离,从而更新删除该条边后分成两个连通块所要链接的最小值。
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e3+10;
const int inf=0x3f3f3f3f;
typedef long long ll;
int n,m,q;
int head[maxn];
int tot;
int dp[maxn][maxn];
bool hav[maxn][maxn];
int fa[maxn];
int dis[maxn][maxn];
struct node{
int u,v,w;
bool operator < (const node a){
return w<a.w;
}
}e[maxn*maxn];
struct Edge{
int to,next;
}edge[maxn<<1];
void addedge(int u,int v){
edge[tot].to=v;
edge[tot].next=head[u];
head[u]=tot++;
}
void init(){
for(int i=0;i<=n;i++){
fa[i]=i;
}
memset(dp,inf,sizeof dp);
memset(dis,inf,sizeof dis);
memset(hav,0,sizeof hav);
memset(head,-1,sizeof head);
tot=0;
}
int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
ll kruskal(){
ll cnt=0,ans=0;
for(int i=0;i<m;i++){
int u=e[i].u,v=e[i].v,w=e[i].w;
int t1=find(u),t2=find(v);
if(t1!=t2){
ans+=(ll)w;
fa[t1]=t2;
cnt++;
addedge(u,v);
addedge(v,u);
hav[u][v]=hav[v][u]=1;
}
if(cnt==n-1) break;
}
return ans;
}
int dfs(int root,int fa,int u,int dep){
int res=inf;
if(dep!=1){
res=min(res,dis[root][u]);
}
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(v==fa) continue;
int tmp=dfs(root,u,v,dep+1);
res=min(res,tmp);
dp[u][v]=dp[v][u]=min(dp[u][v],tmp);
}
return res;
}
int main(){
while(scanf("%d%d",&n,&m)&&(n||m)){
init();
for(int i=0;i<m;i++){
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
dis[e[i].u][e[i].v]=dis[e[i].v][e[i].u]=e[i].w;
}
sort(e,e+m);
ll ans=kruskal();
for(int i=0;i<n;i++){
dfs(i,i,i,0);
}
scanf("%d",&q);
ll res=0;
for(int i=0;i<q;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
if(hav[u][v]){
res+=ans-dis[u][v]+min(dp[u][v],w);
}else{
res+=ans;
}
}
printf("%.4f\n",res*1.0/q);
}
return 0;
}