[BeiJing2010組隊]次小生成樹 Tree
阿新 • • 發佈:2018-10-06
.get fin d+ pac ostream com str program asr
Description
小 C 最近學了很多最小生成樹的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正當小 C 洋洋得意之時,小 P 又來潑小 C 冷水了。小 P 說,讓小 C 求出一個無向圖的次小生成樹,而且這個次小生成樹還得是嚴格次小的,也就是說: 如果最小生成樹選擇的邊集是 EM,嚴格次小生成樹選擇的邊集是 ES,那麽需要滿足:(value(e) 表示邊 e的權值) 這下小 C 蒙了,他找到了你,希望你幫他解決這個問題。
Input
第一行包含兩個整數N 和M,表示無向圖的點數與邊數。 接下來 M行,每行 3個數x y z 表示,點 x 和點y之間有一條邊,邊的權值為z。
Output
包含一行,僅一個數,表示嚴格次小生成樹的邊權和。(數據保證必定存在嚴格次小生成樹)
Sample Input
5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
Sample Output
11
HINT
數據中無向圖無自環;
50% 的數據N≤2 000 M≤3 000;
80% 的數據N≤50 000 M≤100 000;
100% 的數據N≤100 000 M≤300 000 ,邊權值非負且不超過 10^9 。
首先建出最小生成樹,然後對於非樹邊(u,v),它能產生貢獻,當且僅當u,v在樹上路徑上有條邊被切斷了。於是我們用樹剖找出路徑上最長的邊,將其用(u,v)替換,即可得到次小生成樹了。但我們需要得到嚴格次小生成樹,那麽就會出現路徑上最長的邊和w(u,v)相同的情況,這是我們就需要找到一條次小邊,那麽線段樹維護兩個值即可。
記得判掉重邊的影響。。。
/*program from Wolfycz*/ #pragma GCC optimize(3) #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define inf 0x7f7f7f7f using namespace std; typedef long long ll; typedef unsigned int ui; typedef unsigned long long ull; inline int read(){ int x=0,f=1;char ch=getchar(); for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1; for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0'; return x*f; } inline void print(int x){ if (x>=10) print(x/10); putchar(x%10+'0'); } const int N=1e5,M=3e5; int v[N+10],dfn[N+10],ID[N+10]; int n,m; struct S1{ #define ls (p<<1) #define rs (p<<1|1) struct node{ int f,g; node(){f=g=-inf;} void insert(int _f,int _g){f=_f,g=_g;} }tree[(N<<2)+10]; friend node operator +(const node &x,const node &y){ node z; z.f=max(x.f,y.f); z.g=max(x.g,y.g); if (x.f!=y.f) z.g=max(z.g,min(x.f,y.f)); return z; } void build(int p,int l,int r){ if (l==r){ tree[p].insert(v[dfn[l]],-inf); return; } int mid=(l+r)>>1; build(ls,l,mid),build(rs,mid+1,r); tree[p]=tree[ls]+tree[rs]; } node Query(int p,int l,int r,int x,int y){ if (x<=l&&r<=y) return tree[p]; int mid=(l+r)>>1; node res; if (x<=mid) res=res+Query(ls,l,mid,x,y); if (y>mid) res=res+Query(rs,mid+1,r,x,y); return res; } }ST;//Segment Tree struct S2{ int pre[(M<<1)+10],now[N+10],child[(M<<1)+10],val[(M<<1)+10]; int top[N+10],Rem[N+10],deep[N+10],size[N+10],fa[N+10]; int tot,Time; void join(int x,int y,int z){pre[++tot]=now[x],now[x]=tot,child[tot]=y,val[tot]=z;} void insert(int x,int y,int z){join(x,y,z),join(y,x,z);} void build(int x){ deep[x]=deep[fa[x]]+1,size[x]=1; for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){ if (son==fa[x]) continue; fa[son]=x,v[son]=val[p]; build(son),size[x]+=size[son]; if (size[Rem[x]]<size[son]) Rem[x]=son; } } void dfs(int x){ if (!x) return; dfn[ID[x]=++Time]=x; top[x]=Rem[fa[x]]==x?top[fa[x]]:x; dfs(Rem[x]); for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){ if (son==fa[x]||son==Rem[x]) continue; dfs(son); } } int Get(int x,int y,int z){ S1::node res; while (top[x]!=top[y]){ if (deep[top[x]]<deep[top[y]]) swap(x,y); res=res+ST.Query(1,1,n,ID[top[x]],ID[x]);//+被重載了,在ST結構體裏面 x=fa[top[x]]; } if (x!=y){ if (deep[x]>deep[y]) swap(x,y); res=res+ST.Query(1,1,n,ID[Rem[x]],ID[y]); } if (res.f==-inf&&res.g==-inf) return inf;//開始沒判,後面因為重邊導致res兩個信息都為-inf,於是炸int了。。。 return z-(z==res.f?res.g:res.f); } }HLD;//Heavy-Light Decomposition struct S3{ int fa[N+10]; void init(){for (int i=1;i<=n;i++) fa[i]=i;} int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} }DSU;//Disjoint Set Union struct S4{ int x,y,z,type; S4(){type=0;} void insert(){x=read(),y=read(),z=read();} bool operator <(const S4 &a)const{return z<a.z;} }A[M+10]; int main(){ n=read(),m=read(); for (int i=1;i<=m;i++) A[i].insert(); sort(A+1,A+1+m); DSU.init(); ll MST=0;int Ans=inf; for (int i=1;i<=m;i++){ int x=DSU.find(A[i].x),y=DSU.find(A[i].y); if (x!=y) DSU.fa[x]=y,HLD.insert(A[i].x,A[i].y,A[i].z),A[i].type=1,MST+=A[i].z; } HLD.build(1),HLD.dfs(1),ST.build(1,1,n); for (int i=1;i<=m;i++){ if (A[i].type) continue; Ans=min(Ans,HLD.Get(A[i].x,A[i].y,A[i].z)); } printf("%lld\n",MST+Ans); return 0; }
[BeiJing2010組隊]次小生成樹 Tree