小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e的权值) 这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。
小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 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 。
所谓严格的次小是指权值严格大于最小生成树的次小生成树,我们知道一般次小生成树,只需要先用kruskal算法求得最小生成树,然后暴力枚举非树边,替换路径最大边即可。
这题也可以类似思考,只是有一个问题,如果最大边与当前枚举边相等时,我们不能替换,于是求其次用次小边来替换。这样我们需要求得路径上的最小边和次小边(小于最小边),于是我们可以利用LCA的倍增算法来维护。
预处理过程需要考虑i->f[i][j]与f[i][j]->f[f[i][j]][j]这两段的合并,考虑这两段的最大值相同与不同情况,相同则说明次大值是这两个的次大值的最大值,不同的话,假设(a,b),(c,d)表示两段的(最大,次大),若a>c,显然次大为max(b,c), c>a的情况类似,见代码中的函数ck1。
预处理完,维护沿单链向上跳,记单链的(最大,次大)为(a,b),当前得到最优值(lx,ln),分三种情况讨论,lx与a的大小关系,见代码中的函数ck3。
这题数据较弱,其实之前的写法有点小问题也AC了,后来仔细考虑修改后,继续AC,修改后的更趋于正解。
代码:
#include
#include
#include
#include
#define Maxn 300010
using namespace std;
struct edge{
int to,w,next;
}p[Maxn];
int head[Maxn/3],tot;
void addedge(int a,int b,int c){
p[tot].to=b;
p[tot].w=c;
p[tot].next=head[a];
head[a]=tot++;
}
struct line{
int u,v,w;
bool operator<(const line &a)const{
return we) {swap(c,e);swap(d,f);} //c>=1;
}
if(u==v) return ck2(lx,ln,w);
for(int i=18;i>=0;i--){
if(f[u][i]!=f[v][i]){
ck3(lx,ln,u,i);
ck3(lx,ln,v,i);
u=f[u][i];
v=f[v][i];
}
}
ck3(lx,ln,u,0);
ck3(lx,ln,v,0);
return ck2(lx,ln,w);
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i