題意:求嚴格的次小生成樹
嚴格次小生成樹:(value(e)表示邊e的權值) ∑e∈e
m value(e)
s value(e)(em為最小生成樹邊集,es為次小生成樹邊集)
就是次小生成樹邊權和一定要小於最小生成樹, 而非嚴格的就不一定,也可能等於。
非嚴格次小生成樹求法:是在最小生成樹邊集外 找到一條邊(假設兩點為u,v)(一定大於等於最小生成樹里的邊),替換掉
最小生成樹中u,v之間最大邊,使得兩者差值最小。 主要是要儲存最小生成樹兩點之間最大邊。
嚴格次小生成樹與非嚴格相似,但是要儲存兩點之間 最大邊和次大邊, 因為最小生成樹邊集外 的邊等於最大邊時,代替次大邊
否則就是代替最大邊。 而這裡用到了lca最近公共祖先,為了保證聯通
如圖:假設除去紫色的邊是 最小生成樹, 我們的目標是用紫色的邊代替 環中的小於紫邊最大的邊。
所以這裡用到了lca,假設紫邊兩點為u,v, 先找到u,v的最近公共祖先點 fa,就分兩邊 找u,fa之間的最大小於紫邊的邊 和v,fa之間最大小於紫邊的邊
這樣遍歷每條不在最小生成樹中的邊,找到最小的 紫邊與代替邊的差值,加上最小生成樹邊權和就是答案。
最後我們來總結下步驟:
第一步:用kruskal 找到最小生成樹,記算邊權和,和 標記最小生成樹中的邊。
第二步:dfs搜尋 記錄最小生成樹中兩點的最近公共祖先, 並用同樣的方式記錄 兩點之間最大邊和最次邊。
第三步:列舉不在最小生成樹中的邊,lca找到邊兩點的最近公共祖先,分兩邊查詢最大小於這條邊的邊權值。
最後:找到最小的差值加上最小生成樹邊權和即可。
**中一直貫徹著倍增的思想。詳細可以看**:
#include#include#include
#include
#define inf 0x3f3f3f3f
using
namespace
std;
typedef
long
long
ll;
const
int maxn=1e5+100
;const
int maxm=3e5+100
; struct
nodee[
2*maxm];
struct
edgeee[maxm];
int head[maxn],bz[maxn][22];//
bz[i][j]記錄i的2^j祖先
int maxi[maxn][22],mini[maxn][22
],lg[maxn];
//maxi[i][j],記錄i到2^j祖先之間的最大邊權值,mini是記錄次邊值
//lg[i]表示log_2(i)+1,用來優化lca的,可以不用
intdepth[maxn],fa[maxn],n,m,cnt;
bool
cmp(edge a,edge b)
void add(int u,int v,int
w)void dfs(int f,int fath)//
記錄最小生成樹中兩點之間的最近公共祖先和最大邊,次大邊
for(int i=head[f];i!=-1;i=e[i].nxt)
}
}int lca(int x,int y)//
lca求最近公共祖先
int find(int
x)int qmax(int u,int v,int w)//
找到u,v之間小於 w大最大邊
}return
ans;
}int
main()
}dfs(
1,0);
int ans=inf;
for(int i=1;i<=m;i++)//
列舉不在最小生成樹的邊
}printf(
"%lld\n
",sum+ans);//
答案就是最小生成樹邊權和加最小差值
如圖:假設除去紫色的邊是 最小生成樹, 我們的目標是用紫色的邊代替 環中的小於紫邊最大的邊。
所以這裡用到了lca,假設紫邊兩點為u,v, 先找到u,v的最近公共祖先點 fa,就分兩邊 找u,fa之間的最大小於紫邊的邊 和v,fa之間最大小於紫邊的邊
這樣遍歷每條不在最小生成樹中的邊,找到最小的 紫邊與代替邊的差值,加上最小生成樹邊權和就是答案。
最後我們來總結下步驟:
第一步:用kruskal 找到最小生成樹,記算邊權和,和 標記最小生成樹中的邊。
第二步:dfs搜尋 記錄最小生成樹中兩點的最近公共祖先, 並用同樣的方式記錄 兩點之間最大邊和最次邊。
第三步:列舉不在最小生成樹中的邊,lca找到邊兩點的最近公共祖先,分兩邊查詢最大小於這條邊的邊權值。
最後:找到最小的差值加上最小生成樹邊權和即可。
**中一直貫徹著倍增的思想。詳細可以看**:
#include#include#include
#include
#define inf 0x3f3f3f3f
using
namespace
std;
typedef
long
long
ll;
const
int maxn=1e5+100
;const
int maxm=3e5+100
; struct
nodee[
2*maxm];
struct
edgeee[maxm];
int head[maxn],bz[maxn][22];//
bz[i][j]記錄i的2^j祖先
int maxi[maxn][22],mini[maxn][22
],lg[maxn];
//maxi[i][j],記錄i到2^j祖先之間的最大邊權值,mini是記錄次邊值
//lg[i]表示log_2(i)+1,用來優化lca的,可以不用
intdepth[maxn],fa[maxn],n,m,cnt;
bool
cmp(edge a,edge b)
void add(int u,int v,int
w)void dfs(int f,int fath)//
記錄最小生成樹中兩點之間的最近公共祖先和最大邊,次大邊
for(int i=head[f];i!=-1;i=e[i].nxt)
}
}int lca(int x,int y)//
lca求最近公共祖先
int find(int
x)int qmax(int u,int v,int w)//
找到u,v之間小於 w大最大邊
}return
ans;
}int
main()
}dfs(
1,0);
int ans=inf;
for(int i=1;i<=m;i++)//
列舉不在最小生成樹的邊
}printf(
"%lld\n
",sum+ans);//
答案就是最小生成樹邊權和加最小差值
return0;
}
P4180 嚴格次小生成樹 BJWC2010
題目鏈結 當時在暑假早就講了這道題了,只不過我現在才做了這道題。題解 我們要求次小生成樹的話,考慮先把最小生成樹求出來,因為如果我們用求最小生成樹的話,邊早已經從大到小排序好了,所以次小生成樹的就是替換最小生成樹上的一條邊所得。那麼考慮如何來替換那一條邊,要保證嚴格次小,那麼我們需要替換掉最小生成樹...
P4180 BJWC2010 嚴格次小生成樹
題目 最小生成樹都會吧?不會的戳這裡 接下來我們用lca求每一條非樹邊在樹上環的最大邊權和次大邊權,然後求乙個min s mx v,s mx2 v,mn 就可以了,注意如果mx v,第一項不比較.code include include include include include include...
嚴格次小生成樹
顧名思義。生成樹,邊權和嚴格小於最小生成樹 一定和最小生成樹有關係。實際上,有 結論1 嚴格次小生成樹只在最小生成樹上改動一條邊 證明 改動一條邊有意義的話,必然改完這條邊,總和變大。那麼這至少是嚴格次小生成樹的最大值。再改一條也沒有意義。結論2 改動的那條邊w,一定是新加入的那條邊覆蓋的樹鏈的最大...