1. 程式人生 > 實用技巧 >[20200801NOIP提高組模擬T2]電話線鋪設

[20200801NOIP提高組模擬T2]電話線鋪設

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();
}