hdu4126 Genghis Khan the Conqueror 樹形dp+最小生成樹

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章