一道LCA+生成树

BZOJ原题链接

洛谷原题链接

细节挺多,我调了半天。。累炸。。

回到正题,我们先求出随便一棵最小生成树(设边权和为\(s\)),然后扫描剩下所有边,设扫到的边的两端点为\(x,y\),长度为\(z\),树上\(x,y\)间边权最大的边和严格次大的边分别为\(dis_1,dis_2\)。

如果\(z>dis_1\),那么这条边可以替换掉\(dis_1\)对应的边,则得到一个可能答案\(s-dis_1+z\)。

如果\(z=dis_1\),那么这条边可以替换掉\(dis_2\)对应的边,则得到一个可能答案\(s-dis_2+z\)。

然后我们就可以用倍增\(LCA\)快速求树上\(x,y\)间边权最大的边和严格次大的边,定义数组\(g[x][k][0],g[x][k][1]\)表示从节点\(x\)往上跳\(2^k\)下所经过的路径的最大值和严格次小值,然后在预处理\(LCA\)的同时处理即可。

因为调到心态爆炸,所以代码可能比较丑。。

#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
const int M = 3e5 + 10;
const int K = 17;
struct dd {
int x, y, z;
};
dd a[M];
struct aw {
int ma, se_ma;
aw()
{
ma = se_ma = 0;
}
};
int fi[N], di[N << 1], da[N << 1], ne[N << 1], f[N][K], g[N][K][2], de[N], fa[N], l, gn;
bool v[M];
int re()
{
int x = 0;
char c = getchar();
bool p = 0;
for (; c<'0' || c>'9'; c = getchar())
p |= c == '-';
for (; c >= '0'&&c <= '9'; c = getchar())
x = x * 10 + (c - '0');
return p ? -x : x;
}
int comp(dd x, dd y)
{
return x.z < y.z;
}
inline void sw(int &x, int &y)
{
int z = x;
x = y;
y = z;
}
inline int maxn(int x, int y)
{
return x > y ? x : y;
}
inline ll minn(ll x, ll y)
{
return x < y ? x : y;
}
inline int fin(int x)
{
if (!(fa[x] ^ x))
return x;
return fa[x] = fin(fa[x]);
}
inline void add(int x, int y, int z)
{
di[++l] = y;
da[l] = z;
ne[l] = fi[x];
fi[x] = l;
}
void dfs(int x)
{
int i, y;
for (i = 1; i <= gn; i++)
{
f[x][i] = f[f[x][i - 1]][i - 1];
g[x][i][0] = maxn(g[x][i - 1][0], g[f[x][i - 1]][i - 1][0]);
g[x][i][1] = !(g[x][i - 1][0] ^ g[f[x][i - 1]][i - 1][0]) ? maxn(g[x][i - 1][1], g[f[x][i - 1]][i - 1][1]) : (g[x][i - 1][0] > g[f[x][i - 1]][i - 1][0]) ? maxn(g[x][i - 1][1], g[f[x][i - 1]][i - 1][0]) : maxn(g[x][i - 1][0], g[f[x][i - 1]][i - 1][1]);
}
for (i = fi[x]; i; i = ne[i])
{
y = di[i];
if (!de[y])
{
de[y] = de[x] + 1;
f[y][0] = x;
g[y][0][0] = da[i];
g[y][0][1] = -1e9;
dfs(y);
}
}
}
aw fx(aw X, int i, int x)
{
if (X.ma < g[x][i][0])
{
X.se_ma = maxn(X.ma, g[x][i][1]);
X.ma = g[x][i][0];
}
else
if (X.ma > g[x][i][0])
X.se_ma = maxn(g[x][i][0], g[x][i][1]);
return X;
}
aw lca(int x, int y)
{
int i;
aw X;
if (de[x] > de[y])
sw(x, y);
for (i = gn; ~i; i--)
if (de[f[y][i]] >= de[x])
{
X = fx(X, i, y);
y = f[y][i];
}
if (!(x^y))
return X;
for (i = gn; ~i; i--)
if (f[x][i] ^ f[y][i])
{
X = fx(X, i, x);
X = fx(X, i, y);
x = f[x][i];
y = f[y][i];
}
X = fx(X, 0, x);
return X;
}
int main()
{
int i, k = 0, n, m, x, y;
ll s = 0, mi = 1e18;
n = re();
m = re();
gn = log2(n);
for (i = 1; i <= n; i++)
fa[i] = i;
for (i = 1; i <= m; i++)
{
a[i].x = re();
a[i].y = re();
a[i].z = re();
}
sort(a + 1, a + m + 1, comp);
for (i = 1; i <= m; i++)
{
x = fin(a[i].x);
y = fin(a[i].y);
if (x^y)
{
fa[y] = x;
k++;
s += a[i].z;
add(a[i].x, a[i].y, a[i].z);
add(a[i].y, a[i].x, a[i].z);
v[i] = 1;
}
if (!(k ^ (n - 1)))
break;
}
de[1] = 1;
dfs(1);
aw X;
for (i = 1; i <= m; i++)
if (!v[i])
{
X = lca(a[i].x, a[i].y);
if (!(a[i].z^X.ma))
mi = minn(mi, s - X.se_ma + a[i].z);
else
mi = minn(mi, s - X.ma + a[i].z);
}
printf("%lld", mi);
return 0;
}

BZOJ1977或洛谷4180 [BJWC2010]次小生成树的更多相关文章

  1. 洛谷P4180 [BJWC2010]次小生成树(最小生成树,LCT,主席树,倍增LCA,倍增,树链剖分)

    洛谷题目传送门 %%%TPLY巨佬和ysner巨佬%%% 他们的题解 思路分析 具体思路都在各位巨佬的题解中.这题做法挺多的,我就不对每个都详细讲了,泛泛而谈吧. 大多数算法都要用kruskal把最小 ...

  2. 洛谷.4180.[模板]次小生成树Tree(Kruskal LCA 倍增)

    题目链接 构建完MST后,枚举非树边(u,v,w),在树上u->v的路径中找一条权值最大的边(权为maxn),替换掉它 这样在 w=maxn 时显然不能满足严格次小.但是这个w可以替换掉树上严格 ...

  3. bzoj 1977 洛谷P4180 严格次小生成树

    Description: 给定一张N个节点M条边的无向图,求该图的严格次小生成树.设最小生成树边权之和为sum,那么严格次小生成树就是边权之和大于sum的最小的一个 Input: 第一行包含两个整数N ...

  4. 【题解】洛谷P4180 [BJWC2010] 严格次小生成树(最小生成树+倍增求LCA)

    洛谷P4180:https://www.luogu.org/problemnew/show/P4180 前言 这可以说是本蒟蒻打过最长的代码了 思路 先求出此图中的最小生成树 权值为tot 我们称这棵 ...

  5. luogu 4180 严格次小生成树

    次小生成树,顾名思义和次短路的思路似乎很类似呀, 于是就先写了个kruskal(prim不会)跑出最小生成树,给所有路径打标记,再逐个跑最小生成树取大于最小生成树的最小值 50分 #include&l ...

  6. 洛咕P4180 严格次小生成树

    鸽了很久的一道题(?)貌似是去年NOIP前听的emm... 首先我们分析一下最小生成树的性质 我们kruskal建树的时候呢是从小到大贪心加的边,这个的证明用到拟阵.(我太菜了不会) 首先我们不存在连 ...

  7. [洛谷P4341][BJWC2010]外星联络

    题目大意:给你一个长度为$n(n\leqslant3\times10^3)$的字符串,要你求出其中出现次数大于$1$的子串,并按字典序输出次数. 题解:建$SAM$后求出每个点的$size$,最后按字 ...

  8. BZOJ1977/LuoguP4180【模板】严格次小生成树[BJWC2010] (次小生成树)

    这道题本身思维难度不大,但综合性强,细节多 在其上浪一个早上,你的 最小生成树 树链剖分 线段树 DEBUG能力... 都大幅提升 细节与思路都在代码里面了. 欢迎hack. #include< ...

  9. 【洛谷】4180:【模板】严格次小生成树[BJWC2010]【链剖】【线段树维护最大、严格次大值】

    P4180 [模板]严格次小生成树[BJWC2010] 题目描述 小C最近学了很多最小生成树的算法,Prim算法.Kurskal算法.消圈算法等等.正当小C洋洋得意之时,小P又来泼小C冷水了.小P说, ...

随机推荐

  1. python基础学习Day12 生成器、列表推导式、字典的表达式、字典键值对的互换、集合推导式

    一.生成器 1.1 生成器:就是(python)自己用代码写的迭代器,生成器的本质就是迭代器. 1.2 生成器函数 def func1(x): x += print() yield x print() ...

  2. 解决iframe在iphone不兼容的问题

    <div class="scroll-wrapper"> <iframe src="地址"></iframe> </d ...

  3. 05_ssm基础(三)之Spring基础

    11.spring入门引导 12.spring_HelloWord程序 实现步骤: 0.找到spring压缩包,并解压 1.拷贝jar包 2.添加主配置文件(官方文档约28页) 3.在测试中使用 13 ...

  4. 解题6(OutputNMin)

    题目描述 输入n个整数,输出其中最小的k个. 详细描述: 接口说明 原型: bool GetMinK(unsignedint uiInputNum, int * pInputArray, unsign ...

  5. 【C++】Mandelbrot集绘制(生成ppm文件)

    曼德勃罗特集是人类有史以来做出的最奇异,最瑰丽的几何图形.曾被称为"上帝的指纹". 这个点集均出自公式:Zn+1=(Zn)^2+C.(此处Z.C均为复数)所有使得该公式无限迭代后的 ...

  6. powerdesigenr设置主外键颜色

    使用PowerDesigner时,它默认table的字体大小颜色等非常难看: 如果通过 Symbol ---> Format进行设置,只能对选中的最修改,新建的Table无效. 可以通过如下修改 ...

  7. hdu 2089 数位dp

    链接:https://vjudge.net/problem/23625/origin 中文,题目不用说了. 其实这题的数据很小,所以直接暴力也可以过,但是还是要学会数位dp,因为并不是每一题的数据都会 ...

  8. jfinal头像裁剪上传服务器

    前端页面完整代码,复制可用,记得导入库文件 <!DOCTYPE html> <html lang="en"> <head> <title& ...

  9. Python+Selenium学习--上传文件

    场景 文件上传操作也比较常见功能之一,上传功能操作webdriver 并没有提供对应的方法,关键上传文件的思路.上传过程一般要打开一个系统的window 窗口,从窗口选择本地文件添加.所以,一般会卡在 ...

  10. Bitmap byte[] InputStream Drawable 互转

    import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStrea ...