增大边权,求MST,多组询问(只询问,不修改)
做法:1.先求出原图MST,对树中每条边<u,v>,预处理best[u][v]:删除边<u,v>后u所在连通块和v所在连通块通过非树边连接的最短边长
求best[u][v]前还要预处理dp[rt][u]:以rt为根的树,rt到以u为根的子树中所有点通过非树边连接的最短边长
dp[rt][u]和best[u][v]都要通过dfs求出,具体求法见代码
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
#include<vector>
#include<queue>
#define ll long long
#define sf scanf
#define pf printf
#define INF 1<<29
#define mem(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x&(-x)
#define maxn 3010
const ll mol=1000000007;
using namespace std;
int n,m,q;
ll mst,ma[maxn][maxn],dp[maxn][maxn],best[maxn][maxn],ans;
bool vis[maxn][maxn];
struct Node{
int from,to;
ll w;
bool operator<(const Node &rhs)const{
return w<rhs.w;
}
}node[maxn*maxn];
struct Edge{//MST
int to,next;
ll w;
}edge[maxn<<2];
int head[maxn],tot,p[maxn];
void add(int u,int v,ll w){
edge[tot].to=v,edge[tot].w=w,edge[tot].next=head[u],head[u]=tot++;
}
int Find(int x){ return p[x]==x?x:(p[x]=Find(p[x])); }
void kruscal(){
for(int i=1;i<=n;i++) p[i]=i;
sort(node+1,node+m+1);
mem(head,-1),tot=0,mst=0,mem(vis,0);
for(int i=1;i<=m;i++){
int x=Find(node[i].from),y=Find(node[i].to);
if(x!=y){
p[x]=y;
vis[node[i].from][node[i].to]=vis[node[i].to][node[i].from]=1;
add(node[i].from,node[i].to,node[i].w);
add(node[i].to,node[i].from,node[i].w);
mst+=node[i].w;
}
}
}
void dfs1(int u,int fa,int rt){
if(rt!=fa) dp[rt][u]=min(dp[rt][u],ma[u][rt]);
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].to; ll w=edge[i].w;
if(v!=fa){
dfs1(v,u,rt);
dp[rt][u]=min(dp[rt][u],dp[rt][v]);
}
}
}
ll dfs2(int u,int fa,int rt){
ll ans=dp[u][rt];
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(v!=fa){
ans=min(ans,dfs2(v,u,rt));
}
}
return ans;
}
int main(){
//freopen("a.txt","r",stdin);
while(scanf("%d%d",&n,&m)!=EOF,(m&&n)){
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
ma[i][j]=(i==j)?0:INF;
dp[i][j]=best[i][j]=INF;
}
for(int i=1;i<=m;i++){
scanf("%d%d%lld",&node[i].from,&node[i].to,&node[i].w);
node[i].from++,node[i].to++;
ma[node[i].from][node[i].to]=ma[node[i].to][node[i].from]=node[i].w;
}
kruscal();
for(int i=1;i<=n;i++){
dfs1(i,-1,i);//求以i为根的树,i到达树中任意一点为根的子树非树边的最小值
}
for(int u=1;u<=n;u++){
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].to;
best[u][v]=best[v][u]=dfs2(v,u,u);
}
}
scanf("%d",&q);
int qq=q;
ans=0;
while(qq--){
int a,b;
ll c;
scanf("%d%d%lld",&a,&b,&c);
a++,b++;
if(!vis[a][b]) ans+=mst;
else{
if(best[a][b]>c) ans+=(mst+c-ma[a][b]);
else ans+=(mst+best[a][b]-ma[a][b]);
}
}
double answer=(double)ans/(double)q;
printf("%.4f\n",answer);
}
return 0;
}