http://acm.hdu.edu.cn/showproblem.php?pid=4126
给出一个无向图,和Q次修改,每次临时把一条边的的权值增大,然后此时的最小生成树是Wi,然后问Sum(Wi)/q的值。
N=3000,W<=N*N,Q<=10000
先求出原图的最小生成树,然后当询问的x,y并不是最小生成树的相邻的两点的时候,这时候神马也不用动,Wi就是最小生成树的值。当是最小生成树相邻的两个点的时候,这时候我们尝试将这条边删除,然后树被分成了两部分,然后我们要找到最小的一条边,将这两部分相连。然后比较权值就好了
现在问题就是剩下,如何求出树两部分中的最近边。假如求dp[i][j]表示i的子树和j的子树之间的最近距离,那么这样非常不好求。。。参考别人的思路,用dp[i][j]表示i点到j子树的最近距离。这样树形dp一下就能求出来了。。。。然后求两部分之间的最近距离,就枚举其中x部分的所有点i,求min{dp[i][y]}就好了。。。
参考:http://www.chawenti.com/articles/7595.html
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn = 3001;
const int INF = 0x3fffffff;
int n,m;
int g[maxn][maxn];
bool exist[maxn][maxn];
int node[maxn],top;
struct Side{
int to,next,w;
}side[maxn*2];
void add_side(int u,int v,int w){
side[top]=(Side){v,node[u],w};
node[u]=top++;
side[top]=(Side){u,node[v],w};
node[v]=top++;
}
int dis[maxn],from[maxn];
bool vis[maxn];
int dp[maxn][maxn];
int prim(){
int res=0;
top=0;
for(int i=0;i<n;i++){
node[i]=-1;
dis[i]=g[0][i];
from[i]=0;
vis[i]=false;
}
dis[0]=0;
vis[0]=true;
int m,u,v;
for(int i=1;i<n;i++){
m=INF;
for(int j=0;j<n;j++){
if(!vis[j]&&dis[j]<m){
m=dis[j];
u=j;
v=from[j];
}
}
vis[u]=true;
exist[u][v]=exist[v][u]=true;
add_side(u,v,dis[u]);
res+=dis[u];
for(int j=0;j<n;j++){
if(!vis[j]&&g[u][j]<dis[j]){
dis[j]=g[u][j];
from[j]=u;
}
}
}
return res;
}
int get_dp(int u,int fa,int root){
dp[root][u]=INF;
if(fa!=root)dp[root][u]=g[root][u];
for(int i=node[u];i!=-1;i=side[i].next){
int v=side[i].to;
if(v!=fa)dp[root][u]=min(get_dp(v,u,root),dp[root][u]);
}
return dp[root][u];
}
int find_ans(int u,int fa,int root){
int ans=dp[u][root];
for(int i=node[u];i!=-1;i=side[i].next){
int v=side[i].to;
if(v!=fa)ans=min(find_ans(v,u,root),ans);
}
return ans;
}
int main(){
while(~scanf("%d%d",&n,&m)){
if(n+m==0)break;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
g[i][j]=INF;
exist[i][j]=false;
}
}
for(int i=0;i<m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
g[u][v]=g[v][u]=w;
}
int tmp;
tmp=prim();
for(int i=0;i<n;i++)get_dp(i,i,i);
int q;
scanf("%d",&q);
double sum=0.0;
for(int i=0;i<q;i++){
int x,y,c;
scanf("%d%d%d",&x,&y,&c);
if(!exist[x][y]){
sum+=(double)tmp/q;
//printf("%d\n",tmp);
}else{
int t=find_ans(x,y,y);
sum+=(double)min(tmp+c-g[x][y],tmp-g[x][y]+t)/q;
//printf("%d\n",min(tmp+c-g[x][y],tmp-g[x][y]+t));
}
}
printf("%.4f\n",sum);
}
}
/*
5 7
0 1 5
0 4 1
0 3 2
3 4 3
3 1 1
3 2 2
1 2 3
*/