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
*/