「LuoguP4180」 【模板】严格次小生成树[BJWC2010](倍增 LCA Kruscal
题目描述
小C最近学了很多最小生成树的算法,Prim算法、Kurskal算法、消圈算法等等。正当小C洋洋得意之时,小P又来泼小C冷水了。小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是EM,严格次小生成树选择的边集是ES,那么需要满足:(value(e)表示边e的权值) \sum_{e \in E_M}value(e)<\sum_{e \in E_S}value(e)∑e∈EMvalue(e)<∑e∈ESvalue(e)
这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。
输入输出格式
输入格式:
第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。
输出格式:
包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
输入输出样例
说明
数据中无向图无自环; 50% 的数据N≤2 000 M≤3 000; 80% 的数据N≤50 000 M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。
题解
话说这个还没有经典到当模板吧qwq
先做一个最小生成树,图被分成树边和非树边。
在这个树上随便找个点当根,预处理好倍增的东西(包括往上$2^j$步的祖先编号,往祖先走的这条路上最长的边,这条路上第二长的边
然后把每条非树边跑一遍,设两点为$u,v$。
可以想到,从$u$到$v$的这条树上路径中找一条最大但小于这条非树边的边替换,这样的结果是严格次小生成树的一个备选结果。
所以就一边求lca一边跟那一段的最长边和第二长边对比啊什么的。
//记录第二长边主要是防止最长边和这条非树边一样长
作为紫题非常好写,非常不容易错。(毕竟只是倍增lca和Kruscal揉到一起的一个东西,元件都不难写qwq
但确实很长就是了qwq
1 /* 2 qwerta 3 P4180 【模板】严格次小生成树[BJWC2010] Accepted 4 100 5 代码 C++,2.72KB 6 提交时间 2018-10-31 19:50:25 7 耗时/内存 1812ms, 35376KB 8 */ 9 #include<algorithm> 10 #include<iostream> 11 #include<cstring> 12 #include<cstdio> 13 #include<cmath> 14 using namespace std; 15 inline int read() 16 { 17 char ch=getchar(); 18 int x=0; 19 while(!isdigit(ch))ch=getchar(); 20 while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} 21 return x; 22 } 23 const int MAXN=1e5+3,MAXM=3e5+3; 24 struct emm{ 25 int x,y,l,tag; 26 }b[MAXM];//用来存原图,tag表示是否为树边 27 bool cmp(emm qaq,emm qwq){ 28 return qaq.l<qwq.l; 29 }//用来最小生成树 30 int fa[MAXN]; 31 int fifa(int x) 32 { 33 if(fa[x]==x)return x; 34 return fa[x]=fifa(fa[x]); 35 } 36 struct ahh{ 37 int e,f,l; 38 }a[2*MAXN];//用来存树 39 int h[MAXN]; 40 int tot=0; 41 void con(int x,int y,int l)//连树边 42 { 43 a[++tot].f=h[x]; 44 h[x]=tot; 45 a[tot].e=y; 46 a[tot].l=l; 47 a[++tot].f=h[y]; 48 h[y]=tot; 49 a[tot].e=x; 50 a[tot].l=l; 51 return; 52 } 53 int w[MAXN],d[MAXN];//w把边权记在深度较深的点上,d为深度 54 void dfs(int x)//dfs建树 55 { 56 for(int i=h[x];i;i=a[i].f) 57 if(!d[a[i].e]) 58 { 59 fa[a[i].e]=x; 60 d[a[i].e]=d[x]+1; 61 w[a[i].e]=a[i].l; 62 dfs(a[i].e); 63 } 64 return; 65 } 66 int f[MAXN][21];//往上2^j步的祖先 67 int mac[MAXN][21];//往上2^j步途中最长的边长 68 int macc[MAXN][21];//往上2^j步途中严格第二长的边长 69 int zz[5];//辅助数组 70 bool cmpp(int qaq,int qwq){ 71 return qaq>qwq; 72 }//用来给zz排序 73 int main() 74 { 75 //freopen("a.in","r",stdin); 76 int n=read(),m=read(); 77 for(int i=1;i<=m;++i) 78 { 79 b[i].x=read(),b[i].y=read(),b[i].l=read(); 80 } 81 //做个最小生成树↓ 82 sort(b+1,b+m+1,cmp); 83 for(int i=1;i<=n;++i) 84 fa[i]=i; 85 int k=n-1,j=0; 86 long long sum=0; 87 while(k) 88 { 89 j++; 90 int u=fifa(b[j].x),v=fifa(b[j].y); 91 if(u!=v) 92 { 93 fa[u]=v; 94 //cout<<b[j].x<<" "<<b[j].y<<" "<<b[j].l<<endl; 95 b[j].tag=1; 96 sum+=b[j].l;//记下树边权值和 97 con(b[j].x,b[j].y,b[j].l);//把两点连起来 98 k--; 99 } 100 } 101 //dfs建树↓ 102 memset(fa,0,sizeof(fa)); 103 int s=min(n,7); 104 d[s]=1; 105 dfs(s); 106 //倍增↓ 107 for(int i=1;i<=n;++i) 108 { 109 // /cout<<i<<" "<<fa[i]<<endl; 110 f[i][0]=fa[i]; 111 mac[i][0]=w[i]; 112 } 113 for(int j=1;j<=14;++j) 114 for(int i=1;i+(1<<j)-1<=n;++i) 115 { 116 f[i][j]=f[f[i][j-1]][j-1]; 117 zz[1]=mac[i][j-1],zz[2]=macc[i][j-1]; 118 zz[3]=mac[f[i][j-1]][j-1],zz[4]=macc[f[i][j-1]][j-1]; 119 //这里用zz只是懒得写if 120 sort(zz+1,zz+5,cmpp);//从大到小sort一下 121 unique(zz+1,zz+5);//因为要严格第二大所以unique一下 122 mac[i][j]=zz[1]; 123 macc[i][j]=zz[2]; 124 } 125 long long ans=1e14+2333; 126 for(int i=1;i<=m;++i) 127 if(!b[i].tag)//循环每条非树边 128 { 129 int u=b[i].x,v=b[i].y; 130 //倍增求lca 131 if(d[u]<d[v])swap(u,v); 132 for(int j=14;j>=0;--j) 133 if(d[u]-d[v]>=(1<<j)) 134 { 135 if(b[i].l>mac[u][j])//如果非树边比最长边长 136 ans=min(ans,sum-mac[u][j]+b[i].l);//这两条替换是一种可能方案 137 else if(b[i].l==mac[u][j])//否则如果非树边跟最长边一样长 138 ans=min(ans,sum-macc[u][j]+b[i].l);//把这条边跟第二长边替换是一种可能方案 139 u=f[u][j];//记得往上跳 140 } 141 if(u==v)continue; 142 for(int j=14;j>=0;--j) 143 if(f[u][j]!=f[v][j]) 144 { 145 if(b[i].l>mac[u][j]) 146 ans=min(ans,sum-mac[u][j]+b[i].l); 147 else if(b[i].l==mac[u][j]) 148 ans=min(ans,sum-macc[u][j]+b[i].l); 149 u=f[u][j]; 150 if(b[i].l>mac[v][j]) 151 ans=min(ans,sum-mac[v][j]+b[i].l); 152 else if(b[i].l==mac[v][j]) 153 ans=min(ans,sum-macc[v][j]+b[i].l); 154 v=f[v][j]; 155 } 156 if(b[i].l>w[u]) 157 ans=min(ans,sum-w[u]+b[i].l); 158 if(b[i].l>w[v]) 159 ans=min(ans,sum-w[v]+b[i].l); 160 } 161 cout<<ans<<endl;//输出撒花 162 return 0; 163 }