[20200801NOIP提高组模拟T2]电话线铺设 Description solution code
给定(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();
}