[20200801NOIP提高組模擬T2]電話線鋪設
阿新 • • 發佈:2020-08-01
Description
給定\(n\)點,\(w\)條帶權白邊,\(l\)條帶權黑邊.現需用\(n-2\)條白邊和\(1\)條黑邊構造出\(MST\).請輸出\(MST\)大小及所用的白邊編號和黑邊編號.(注:有可能光用白邊構造不出\(MST\))
solution
做這題的時候挺坎坷的.先是打了45pts暴力,然後想出正解但沒時間也不想打了.
其實思路很簡單.對於光用白邊構造不出\(MST\)的點,我們直接白邊儘量構造\(MST\),然後黑邊補充即可.對於其它資料點,我們可以先用白邊構造\(MST\),然後再一一列舉每條黑邊.設黑邊端點為\(x,y\),則我們可以斷掉\(MST\)上\(<x,y>\) 之間路徑上權值最大的邊,然後將邊\(x,y\)連上即可.試問如何找最大權值邊?樹上倍增即可.
code
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #include<map> #define R register #define next kdjadskfj #define debug puts("mlg") #define mod 1000000007 #define Mod(x) ((x%mod+mod)%mod) using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; inline ll read(); inline void write(ll x); inline void writeln(ll x); inline void writesp(ll x); struct node{ ll x,y,z,name1; bool operator <(node const &X)const{return z<X.z;} }W[220000],B[220000]; ll n,w,l; ll f[220000]; ll sum,ans,Ans; bool vis[220000]; ll head[550000],next[550000],to[550000],tot,c[550000]; ll d[210000],F[210000][20],M[210000][20]; ll AnsL,AnsR,Maxn,tag,pre[210000],last[210000]; inline ll getf(ll x){return f[x]==x?x:f[x]=getf(f[x]);} inline void merge(ll x,ll y){f[getf(x)]=getf(y);} inline bool check(ll x,ll y){return getf(x)==getf(y);} inline void solve1(){ Maxn=((ull)1<<63)-1; for(R ll i=1;i<=l;i++){ if(!check(B[i].x,B[i].y)){ if(Maxn>ans+B[i].z){ Maxn=ans+B[i].z; Ans=B[i].name1; } } } ans=Maxn; writeln(ans); for(R ll i=1;i<=w;i++) if(vis[i]) writeln(i); writeln(Ans); exit(0); } inline void add(ll x,ll y,ll z){ to[++tot]=y;next[tot]=head[x];head[x]=tot;c[tot]=z; } inline void dfs1(ll x,ll fa,ll deep){ d[x]=deep; F[x][0]=fa; for(R ll i=head[x],ver;i;i=next[i]){ ver=to[i]; if(ver==fa) continue; M[ver][0]=c[i]; dfs1(ver,x,deep+1); } } inline void bz(){ for(R ll k=1;k<=19;k++) for(R ll i=1;i<=n;i++) F[i][k]=F[F[i][k-1]][k-1],M[i][k]=max(M[i][k-1],M[F[i][k-1]][k-1]); } inline ll lca(ll x,ll y){ ll maxn=0; if(d[x]<d[y]) swap(x,y); for(R ll i=19;i>=0;i--){ if(d[F[x][i]]>=d[y]){ maxn=max(M[x][i],maxn); x=F[x][i]; } } if(x==y) return maxn; for(R ll i=19;i>=0;i--){ if(F[x][i]!=F[y][i]){ maxn=max(maxn,max(M[x][i],M[y][i])); x=F[x][i];y=F[y][i]; } } maxn=max(maxn,max(M[x][0],M[y][0])); //這裡還未跳到lca,所以還得再加最後一層判斷.(一開始沒加調了好久bug) return maxn; } inline void dfs2(ll x,ll fa){ for(R ll i=head[x],ver;i;i=next[i]){ ver=to[i]; if(ver==fa) continue; dfs2(ver,x); pre[ver]=x;last[ver]=i; } } inline void solve2(){ dfs1(1,0,1);bz(); Maxn=(((ull)1<<63)-1); for(R ll i=1;i<=l;i++){ ll len=lca(B[i].x,B[i].y); if(ans-len+B[i].z<Maxn){ Maxn=ans-len+B[i].z; AnsL=B[i].x;AnsR=B[i].y; Ans=B[i].name1; tag=len; } } dfs2(AnsL,0); for(R ll x=AnsR;x;x=pre[x]){ if(c[last[x]]==tag){ for(R ll i=1;i<=w;i++) if(min(W[i].x,W[i].y)==min(x,pre[x])&&max(W[i].x,W[i].y)==max(x,pre[x])&&W[i].z==tag){vis[W[i].name1]=false;break;} break; } } writeln(Maxn); for(R ll i=1;i<=w;i++) if(vis[i]) writeln(i); writeln(Ans); exit(0); } int main(){ freopen("telephone.in","r",stdin); freopen("telephone.out","w",stdout); n=read();w=read();l=read(); for(R ll i=1;i<=w;i++){W[i].x=read();W[i].y=read();W[i].z=read();W[i].name1=i;} sort(W+1,W+w+1); for(R ll i=1;i<=l;i++){B[i].x=read();B[i].y=read();B[i].z=read();B[i].name1=i;} for(R ll i=1;i<=n;i++)f[i]=i; for(R ll i=1;i<=w;i++){ if(!check(W[i].x,W[i].y)){ ++sum; merge(W[i].x,W[i].y); ans+=W[i].z; vis[W[i].name1]=true; add(W[i].x,W[i].y,W[i].z); add(W[i].y,W[i].x,W[i].z); } if(sum==n-1) break; } if(sum!=n-1) solve1(); else solve2(); }