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

posted @ 2020-08-01 18:41  月落乌啼算钱  阅读(156)  评论(0编辑  收藏  举报