1. 程式人生 > >最小生成樹+樹上任意兩點的最小期望

最小生成樹+樹上任意兩點的最小期望

lan ble 最小 那種 頂點 type 記錄 解題思路 並查集

參考博客

題意:有n個城市,m條需要被重建的路,每條路有權值且各不相同,讓你使所有城市相通(直接或間接)的情況下,找出權值和最小的那種方案,然後問你從這種方案中任意選取兩點,問這兩點之間的距離的最小期望值是多少。
解題思路:讓你找出權值和最小,當然是最小生成樹啊,但是後面那個最小期望有點麻煩,不過我們可以仔細分析得到,每條路的權值都不一樣所得出的最小生成樹是唯一的(具體證明自己百度),那麽生成樹唯一,怎樣在這棵樹上求最小期望,最小期望?這其實是出題人忽悠你的,一棵樹上任意兩點之間的距離唯一,所以不存在最小最大之說,只要找出任意兩兩點之間的距離就行,然後把所有的距離加起來除以n*(n - 1)/2就行,那麽問題就轉化為在一無根樹上求任意兩點之間的距離之和,我們分析可以得到,所有距離之和等於每一條邊的權值乘以這條邊出現的次數之和,每一條邊的權值我們知道,我們現在要知道每一條邊出現過多少次,而我們可以發現,每條邊出現的次數等於這條邊的兩個端點兩側`頂點數目的乘積,知道了這一點,我們就可以直接dfs了,任意選取一個頂點為根(我這裏選的是1),令dp[u]等於以u為根的子樹的頂點數,然後枚舉每一條邊,假如一條邊的兩個頂點是x1,x2,那麽x1,x2中要麽x1是x2的父親,要麽x2是x1的父親,那麽到底誰是誰的父親,很簡單,比較dp[x1],dp[x2],小的那個一定是孩子,所有這條邊的一側的頂點數為v1 = min(dp[x1],dp[x2]),那麽另一側頂點數是多少呢?很簡單,總數是n ,所有另一側頂點數v2 = n - v1,所有這條邊出現的次數為v1v2,註意最後求期望除以n*(n - 1)/2時一定要將n,n - 1變成long long 之後再求,我在這裏wa了無數次。

代碼:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<string.h>
typedef long long ll;
using namespace std;
const int max_=1e6+5;
const int max_n=1e5+5;
struct edge{
     int form;
     int to;
     ll w;
};
struct edge tu[max_],tree[max_n];
int dp[max_n];//記錄包自身的子樹節點 int fa[max_n];//父節點 bool vis[max_n];//標記數組 vector<int>g[max_n];//記錄子節點 int tot1,tot2; void add_edge1(int x,int y,ll w)//建圖 { tu[tot1].form=x; tu[tot1].to=y; tu[tot1++].w=w; } void add_edge2(int x,int y,ll w)//建樹 { tree[tot2].form=x; tree[tot2].to=y; tree[tot2
++].w=w; } bool cmp(edge a,edge b)//排序 { return a.w<b.w; } int find_fa(int x)//並查集 { if(x==fa[x]) return x; else return fa[x]=find_fa(fa[x]); } ll Kruskal(int n)//最小生成樹算法 { sort(tu,tu+tot1,cmp); int ans=0; ll ant=0; for(int i=0;i<tot1;i++) { int v=tu[i].form; int u=tu[i].to; ll w=tu[i].w; int f1=find_fa(v); int f2=find_fa(u); if(f1!=f2) { fa[f1]=f2; ant+=w; add_edge2(u,v,w); ans++; g[u].push_back(v); g[v].push_back(u); } if(ans==n-1) { return ant; } } } void dfs(int x)//dfs尋找樹的子節點 { vis[x]=true; dp[x]=1; for(int i=0;i<g[x].size();i++) { int y=g[x][i]; if(!vis[y]) { dfs(y); dp[x]+=dp[y]; } } } void init(int n)//初始化 { memset(vis,0,sizeof(vis)); memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++) { fa[i]=i; g[i].clear(); } tot1=0; tot2=0; } int main() { int t; scanf("%d",&t); while(t--) { int n,m; scanf("%d %d",&n,&m); ll ans=0; init(n); if(m==0) { printf("0 0.00\n"); continue; } while(m--) { int x,y; ll w; scanf("%d %d %lld",&x,&y,&w); add_edge1(x,y,w); } ll sum=Kruskal(n); dfs(1); for(int i=0;i<tot2;i++) { int u=tree[i].form; int v=tree[i].to; ll w=tree[i].w; int k=min(dp[v],dp[u]); ans+=w*(n-k)*k; //cout<<ans<<endl; } // printf("%.2f\n",1.0*(n-1)*n/2); double expe=ans*1.0/(1.0*n*(n-1)/2); printf("%lld %.2lf\n",sum,expe); } }

最小生成樹+樹上任意兩點的最小期望