(luogu4180) [Beijing2010组队]次小生成树Tree
生活随笔
收集整理的這篇文章主要介紹了
(luogu4180) [Beijing2010组队]次小生成树Tree
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
嚴格次小生成樹
首先看看如果不嚴格我們怎么辦。
非嚴格次小生成樹怎么做
由此,我們發現一個結論,求非嚴格次小生成樹,只需要先用kruskal算法求得最小生成樹,然后暴力枚舉非樹邊,替換路徑最大邊即可。
那要是嚴格呢?
我們發現如果是嚴格的次小生成樹,那么將一條邊替換另一條時,這兩條邊的權值一定不相同
但是,我們知道,替換邊肯定大于等于被替換邊(因為如果替換邊小于被替換邊,就存在一顆包含替換邊而不包含被替換邊的一棵權值更小的生成樹,原樹就不是最小生成樹了)
所以替換邊要么等于路徑上最大的邊,要么比最大的邊還大。
利用這個性質,我們只需要維護路徑中的最大值和次大值,當替換邊等于路徑上的最大值,我們直接換用嚴格次大值即可。
一些細節
1.我維護兩點之間路徑最大值用的是LCT,但是正解是LCA。LCT必須要開O2才能跑過去。
2.數組要開足夠大,最后統計答案時要開long long,不然會爆int
我的代碼
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <vector> #include <queue> #define rg register int #define ll long long #define RG register #define il inline using namespace std;il ll gi() {RG ll x=0;rg o=0;RG char ch=getchar();while(ch!='-'&&(ch<'0'||'9'<ch)) ch=getchar();if(ch=='-') o=1,ch=getchar();while('0'<=ch&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar();return o?-x:x; } int n,m; #define SZ 7000010struct Edge {int a,b;ll w;}e[SZ]; bool cmp(Edge a,Edge b) {return a.w<b.w;}#define lson tr[x].ch[0] #define rson tr[x].ch[1] struct Splaytree{int fa,ch[2],rev,mxp,mxp2;}tr[SZ]; il void pushup(rg x) {tr[x].mxp=x; tr[x].mxp2=0;if(e[tr[lson].mxp].w>e[tr[x].mxp].w) tr[x].mxp=tr[lson].mxp;if(e[tr[rson].mxp].w>e[tr[x].mxp].w) tr[x].mxp=tr[rson].mxp;// 維護 最大if(e[tr[lson].mxp].w!=e[tr[x].mxp].w && (e[tr[lson].mxp].w>e[tr[x].mxp].w||!tr[x].mxp2) ) tr[x].mxp2=tr[lson].mxp;if(e[tr[lson].mxp2].w!=e[tr[x].mxp].w && (e[tr[lson].mxp2].w>e[tr[x].mxp].w||!tr[x].mxp2) ) tr[x].mxp2=tr[lson].mxp2; if(e[tr[rson].mxp].w!=e[tr[x].mxp].w && (e[tr[rson].mxp].w>e[tr[x].mxp].w||!tr[x].mxp2) ) tr[x].mxp2=tr[rson].mxp;if(e[tr[rson].mxp2].w!=e[tr[x].mxp].w && (e[tr[rson].mxp2].w>e[tr[x].mxp].w||!tr[x].mxp2) ) tr[x].mxp2=tr[rson].mxp2; // 維護嚴格次大 } il void pushdown(rg x) {if(tr[x].rev){tr[lson].rev^=1,tr[rson].rev^=1;swap(lson,rson),tr[x].rev=0;} } il bool isroot(rg x) {return tr[tr[x].fa].ch[0]!=x && tr[tr[x].fa].ch[1]!=x; } il void rotate(rg x) {rg y=tr[x].fa,z=tr[y].fa;rg k=tr[y].ch[1]==x;if(!isroot(y)) tr[z].ch[y==tr[z].ch[1]]=x;tr[x].fa=z;tr[y].ch[k]=tr[x].ch[k^1],tr[tr[x].ch[k^1]].fa=y;tr[x].ch[k^1]=y,tr[y].fa=x;pushup(y),pushup(x); } int stk[SZ],top; il void splay(rg x) {stk[top=1]=x;for(rg i=x;!isroot(i);i=tr[i].fa) stk[++top]=tr[i].fa;for(;top;--top) pushdown(stk[top]);while(!isroot(x)){rg y=tr[x].fa,z=tr[y].fa;if(!isroot(y))(tr[y].ch[0]==x)^(tr[z].ch[0]==y)?rotate(x):rotate(y);rotate(x);} } il void access(rg x) {for(rg y=0;x;y=x,x=tr[x].fa)splay(x),rson=y,pushup(x);} il void makeroot(rg x) {access(x);splay(x);tr[x].rev^=1;} il int findroot(rg x) {access(x);splay(x);while(lson) x=lson;return x;} il void split(rg x,rg y) {makeroot(x);access(y);splay(y);} il int query(rg x,rg y) {split(x,y);return tr[y].mxp;} //求x 到 y最大值 il int query2(rg x,rg y) {split(x,y);return tr[y].mxp2;} // 求x 到 y嚴格次大值 il void link(rg x,rg y) {makeroot(x);tr[x].fa=y;} il void cut(rg x,rg y) {split(x,y);if(tr[y].ch[0]==x)tr[y].ch[0]=tr[x].fa=0;} int fa[SZ];int find_fa(rg x) {if(x!=fa[x]) fa[x]=find_fa(fa[x]);return fa[x];} //一行并查集 bool check[SZ];int main() {n=gi(),m=gi();for(rg i=1;i<=m;++i) e[i]=(Edge){gi(),gi(),gi()};// 先求一遍最小生成樹 ans 記錄最小生成樹邊的大小RG ll ans=0;sort(e+1,e+1+m,cmp); for(rg i=1;i<=n;++i) fa[i]=i; // 初始化并查集for(rg f1,f2,i=1;i<=m;++i){f1=find_fa(e[i].a);f2=find_fa(e[i].b);if(f1!=f2){check[i]=1; // check=1 表示最小生成樹中有這一條邊 反之fa[f1]=f2;ans+=e[i].w;link(e[i].a+m,i);link(e[i].b+m,i);}}#define INF 2147483647 #define Getmin(a,b) (a)=(a)>(b)?(b):(a) RG ll Ans=INF;for(rg f1,f2,i=1;i<=m;++i){if(check[i]) continue; //我們選擇不再最小生成樹上的邊rg mxp=query(e[i].a+m,e[i].b+m);if(e[mxp].w==e[i].w){rg mxp2=query2(e[i].a+m,e[i].b+m);if(!mxp2 || e[mxp2].w==e[mxp].w) continue;Getmin(Ans,e[i].w-e[mxp2].w);}else Getmin(Ans,e[i].w-e[mxp].w);}cout<<ans+Ans;return 0; }轉載于:https://www.cnblogs.com/tply/p/8423955.html
總結
以上是生活随笔為你收集整理的(luogu4180) [Beijing2010组队]次小生成树Tree的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【洛谷】P3919 【模板】可持久化线段
- 下一篇: iframe 页面刷新