严格次小生成树[BJWC2010] (树链剖分,倍增,最小生成树)
题目链接
Solution
有几点关键,首先,可以证明次小生成树一定是由最小生成树改变一条边而转化来.
所以需要枚举所有非最小生成树的边\((u,v)\).并且找到 \(u\) 到 \(v\) 的边中最大边和次大边.
为什么要找次大边呢?? 因为可能最大边与要替换的边长度相等,那么这种条件生成的便不是严格的次小生成树.
然后找到 \(u,v\) 之间的次大和最大边有两种方式:
- 树链剖分+线段树维护
剖分最小生成树,然后用线段树维护.
此时线段树节点转移时要考虑左右节点的次大和最大的 \(4\) 个值.
时间复杂度: \(O(mlogn)\) . - 树上 \(st\) 表
与倍增求 \(LCA\) 的方式类似,倍增维护信息然后找 \(LCA\) 即可.
时间复杂度: \(O(mlogn)\) .
然后似乎还可以用 \(LCT\) 来做.时间复杂度也差不多.
Code
倍增版本:
#include<bits/stdc++.h>
#define N 400010
#define M 900010
#define INF 2147483647000000
#define ll long long
using namespace std;
struct edge{
ll u,v,d;
ll next;
}G[N<<1];
ll tot=0;
ll head[N];
inline void addedge(ll u,ll v,ll d)
{
G[++tot].u=u,G[tot].v=v,G[tot].d=d,G[tot].next=head[u],head[u]=tot;
G[++tot].u=v,G[tot].v=u,G[tot].d=d,G[tot].next=head[v],head[v]=tot;
}
ll bz[N][19];
ll maxi[N][19];
ll mini[N][19];
ll deep[N];
inline void dfs(ll u,ll fa)
{
bz[u][0]=fa;
for(ll i=head[u];i;i=G[i].next)
{
ll v=G[i].v;
if(v==fa)continue;
deep[v]=deep[u]+1ll;
maxi[v][0]=G[i].d;
mini[v][0]=-INF;
dfs(v,u);
}
}
ll n;
inline void cal()
{
for(ll i=1;i<=18;++i)
for(ll j=1;j<=n;++j)
{
bz[j][i]=bz[bz[j][i-1]][i-1];
maxi[j][i]=max(maxi[j][i-1],maxi[bz[j][i-1]][i-1]);
mini[j][i]=max(mini[j][i-1],mini[bz[j][i-1]][i-1]);
if(maxi[j][i-1]>maxi[bz[j][i-1]][i-1])mini[j][i]=max(mini[j][i],maxi[bz[j][i-1]][i-1]);
else if(maxi[j][i-1]<maxi[bz[j][i-1]][i-1])mini[j][i]=max(mini[j][i],maxi[j][i-1]);
}
}
inline ll LCA(ll x,ll y)
{
if(deep[x]<deep[y])swap(x,y);
for(ll i=18;i>=0;--i)
if(deep[bz[x][i]]>=deep[y])
x=bz[x][i];
if(x==y)return x;
for(ll i=18;i>=0;--i)
if(bz[x][i]^bz[y][i])
x=bz[x][i],y=bz[y][i];
return bz[x][0];
}
inline ll qmax(ll u,ll v,ll maxx)
{
ll Ans=-INF;
for(ll i=18;i>=0;--i)
{
if(deep[bz[u][i]]>=deep[v])
{
if(maxx!=maxi[u][i])Ans=max(Ans,maxi[u][i]);
else Ans=max(Ans,mini[u][i]);
u=bz[u][i];
}
}
return Ans;
}
inline void read(ll &x)
{
x=0;
char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+(ch^48),ch=getchar();
}
ll m;
edge A[M<<1];
inline bool cmp(edge x,edge y)
{
return x.d<y.d;
}
ll Father[N];
inline ll Get_Father(ll x)
{
return (x==Father[x]) ? x : Father[x]=Get_Father(Father[x]);
}
bool B[M<<1];
int main()
{
read(n),read(m);
for(ll i=1;i<=m;++i)
{
read(A[i].u),read(A[i].v),read(A[i].d);
}
sort(A+1,A+m+1,cmp);
for(ll i=1;i<=n;++i)
Father[i]=i;
ll Cnt=0ll;
for(ll i=1;i<=m;++i)
{
ll Father_u=Get_Father(A[i].u);
ll Father_v=Get_Father(A[i].v);
if(Father_u!=Father_v)
{
Cnt+=A[i].d;
Father[Father_u]=Father_v;
addedge(A[i].u,A[i].v,A[i].d);
B[i]=true;
}
}
mini[1][0]=-INF;
deep[1]=1;
dfs(1,-1);
cal();
ll Ans=INF;
for(ll i=1;i<=m;++i)
{
if(!B[i])
{
ll u=A[i].u;
ll v=A[i].v;
ll d=A[i].d;
ll lca=LCA(u,v);
ll maxu=qmax(u,lca,d);
ll maxv=qmax(v,lca,d);
Ans=min(Ans,Cnt-max(maxu,maxv)+d);
}
}
printf("%lld",Ans);
return 0;
}
树剖版本:
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#define MAXN 100010
#define MAXM 300010
#define pos(l, r) ((l+r) | (l != r))
using namespace std;
struct Edge {
int u, v, w, f, next;
bool operator < (const Edge &A) const { return w < A.w; }
} e[MAXM*2];
struct node {
int m1, m2;
} t[MAXN*2];
int n, m, h[MAXN], dep[MAXN], son[MAXN], w[MAXN], tot, fa[MAXN], par[MAXN], cnt, id[MAXN], top[MAXN], a[MAXN], TOT;
long long MST, secMST = 1e15;
inline int max(int a, int b) {
return a > b ? a : b;
}
inline int secmax(int a, int b, int c, int d) {
int tmp[4] = {a, b, c, d};
sort(tmp, tmp+4);
for (int i = 2; i >= 0; --i) {
if (tmp[i] != tmp[i+1]) return tmp[i];
}
}
void addEdge(int ui, int vi, int wi, int fi) {
e[++tot] = (Edge) {ui, vi, wi, fi, h[ui]};
h[ui] = tot;
}
int find(int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void dfs(int u) {
w[u] = 1;
for (int i = h[u]; i; i = e[i].next) {
if (!w[e[i].v]) {
dep[e[i].v] = dep[u]+1;
par[e[i].v] = u;
a[e[i].v] = e[i].w;
dfs(e[i].v);
w[u] += w[e[i].v];
if (w[son[u]] < w[e[i].v]) son[u] = e[i].v;
}
}
}
void init(int u, int p) {
id[u] = ++cnt;
top[u] = p;
if (son[u]) init(son[u], p);
for (int i = h[u]; i; i = e[i].next) {
if (!top[e[i].v]) init(e[i].v, e[i].v);
}
}
void modify(int l, int r, int x, int d, int p) {
if (l == r) {
t[p].m1 = d;
t[p].m2 = 0;
} else {
int mid = (l+r)>>1, lc = pos(l, mid), rc = pos(mid+1, r);
if (x <= mid) modify(l, mid, x, d, lc);
else modify(mid+1, r, x, d, rc);
t[p].m2 = secmax(t[lc].m1, t[lc].m2, t[rc].m1, t[rc].m2);
t[p].m1 = max(t[lc].m1, t[rc].m1);
}
}
node query(int l, int r, int x, int y, int p) {
if (x <= l && r <= y) return t[p];
int mid = (l+r)>>1, lc = pos(l, mid), rc = pos(mid+1, r);
if (x <= mid && y > mid) {
node t1 = query(l, mid, x, y, lc), t2 = query(mid+1, r, x, y, rc);
return (node) {max(t1.m1, t2.m1), secmax(t1.m1, t1.m2, t2.m1, t2.m2)};
} else if (x <= mid) return query(l, mid, x, y, lc);
else return query(mid+1, r, x, y, rc);
}
node solve(int u, int v) {
int pu = top[u], pv = top[v];
node res = (node) {0, 0};
while (pu != pv) {
if (dep[pu] < dep[pv]) {
swap(pu, pv);
swap(u, v);
}
node tmp = query(1, n, id[pu], id[u], pos(1, n));
res.m2 = secmax(res.m1, res.m2, tmp.m1, tmp.m2);
res.m1 = max(res.m1, tmp.m1);
u = par[pu];
pu = top[u];
}
if (u == v) return res;
if (dep[u] < dep[v]) swap(u, v);
node tmp = query(1, n, id[v]+1, id[u], pos(1, n));
res.m2 = secmax(res.m1, res.m2, tmp.m1, tmp.m2);
res.m1 = max(res.m1, tmp.m1);
return res;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1, ui, vi, wi; i <= m; ++i) {
scanf("%d%d%d", &ui, &vi, &wi);
addEdge(ui, vi, wi, 0);
}
sort(e+1, e+tot+1);
TOT = tot;
memset(h, 0, sizeof(h));
for (int i = 1; i <= n; ++i) fa[i] = i;
for (int i = 1, k; i <= TOT; ++i) {
int ux = find(e[i].u), uy = find(e[i].v);
if (ux != uy) {
fa[ux] = uy;
MST += e[i].w;
e[i].f = 1;
e[i].next = h[e[i].u];
h[e[i].u] = i;
addEdge(e[i].v, e[i].u, e[i].w, 1);
k++;
}
if (k == n-1) break;
}
dfs(1);
init(1, 1);
for (int i = 1; i <= n; ++i) modify(1, n, id[i], a[i], pos(1, n));
for (int i = 1; i <= TOT; ++i) {
if (!e[i].f) {
node cross = solve(e[i].u, e[i].v);
long long tmp = MST+e[i].w-(cross.m1 == e[i].w ? cross.m2 : cross.m1);
if (tmp > MST && tmp < secMST) secMST = tmp;
}
}
printf("%lld\n", secMST);
return 0;
}
严格次小生成树[BJWC2010] (树链剖分,倍增,最小生成树)的更多相关文章
- BZOJ 2243: [SDOI2011]染色 树链剖分 倍增lca 线段树
2243: [SDOI2011]染色 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/pr ...
- 【洛谷】4180:【模板】严格次小生成树[BJWC2010]【链剖】【线段树维护最大、严格次大值】
P4180 [模板]严格次小生成树[BJWC2010] 题目描述 小C最近学了很多最小生成树的算法,Prim算法.Kurskal算法.消圈算法等等.正当小C洋洋得意之时,小P又来泼小C冷水了.小P说, ...
- BZOJ 3083: 遥远的国度 dfs序,树链剖分,倍增
今天再做一天树的题目,明天要开始专攻图论了.做图论十几天之后再把字符串搞搞,区域赛前再把计几看看. 3083: 遥远的国度 Time Limit: 10 Sec Memory Limit: 128 ...
- HDU3710 Battle over Cities(最小生成树+树链剖分+倍增+线段树)
Battle over Cities Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Othe ...
- CF504E Misha and LCP on Tree 后缀自动机+树链剖分+倍增
求树上两条路径的 LCP (树上每个节点代表一个字符) 总共写+调了6个多小时,终于过了~ 绝对是我写过的最复杂的数据结构了 我们对这棵树进行轻重链剖分,然后把所有的重链分正串,反串插入到广义后缀自动 ...
- 【树链剖分/倍增模板】【洛谷】3398:仓鼠找sugar
P3398 仓鼠找sugar 题目描述 小仓鼠的和他的基(mei)友(zi)sugar住在地下洞穴中,每个节点的编号为1~n.地下洞穴是一个树形结构.这一天小仓鼠打算从从他的卧室(a)到餐厅(b),而 ...
- BZOJ 3083 树链剖分+倍增+线段树
思路: 先随便选个点 链剖+线段树 1操作 就直接改root变量的值 2操作 线段树上改 3操作 分成三种情况 1.new root = xx 整个子树的min就是ans 2. lca(new roo ...
- Educational Codeforces Round 3 E. Minimum spanning tree for each edge (最小生成树+树链剖分)
题目链接:http://codeforces.com/contest/609/problem/E 给你n个点,m条边. 问枚举每条边,问你加这条边的前提下组成生成树的权值最小的树的权值和是多少. 先求 ...
- BZOJ 1977: [BeiJing2010组队]次小生成树 Tree( MST + 树链剖分 + RMQ )
做一次MST, 枚举不在最小生成树上的每一条边(u,v), 然后加上这条边, 删掉(u,v)上的最大边(或严格次大边), 更新答案. 树链剖分然后ST维护最大值和严格次大值..倍增也是可以的... - ...
随机推荐
- C++ Stack 与String
// ConsoleApplication1.cpp : 此文件包含 "main" 函数.程序执行将在此处开始并结束. // #include "pch.h" ...
- Dede技巧
解决DEDE图集上传图片时跳出302错误 本地上传图集的时候突然提示网页出错,还爆出302错误. 解决办法是在include/userlogin.class.php文件中的第二行session_s ...
- java子父类初始化顺序 (1)父类静态代码块(2)父类静态变量初始化(3)子类静态代码块(4)子类静态变量初始化(5)main(6)有对象开辟空间都为0(7)父类显示初始化(8)父类构造(9)子类显示初始化(10)子类构造
标题 静态代码块与静态成员变量还要看代码的先后顺序 看程序,说出结果 结果为: x=0 看程序,说出结果 结果如下: 补充 : 静态代码块:static{ } 在JVM加载时即执行,先于主方法执行,用 ...
- 获得函数返回值类型、参数tuple、成员函数指针中的对象类型
//function_traits.h,获得函数返回值类型.参数tuple.成员函数指针中的对象类型 //参考https://github.com/qicosmos/cosmos/blob/maste ...
- destoon 屏蔽会员组,让个人,游客不显示
include/post.fun.php 文件的group_select函数增加 排除参数 except function group_select($name = 'groupid', $titl ...
- 【windows】共享文件夹设置
控制面板\网络和 Internet\网络和共享中心\高级共享设置 在当前的域\公用\家庭网络 下文件和打印机共享开关打开 现在可以在 计算机-网络 里面看到共享的计算机啦 选中自己想要分享的文件,右键 ...
- 将php数组转js数组,js如何接收PHP数组,json的用法
首先下载下面这个文件(这是一段是别人写出来专门解析json的代码),然后引入这个文件! http://pan.baidu.com/s/1dD8qVr7 现在当我们需要用ajax与后台进行交互时,怎样将 ...
- UVA - 1152 4 Values whose Sum is 0问题分解,二分查找
题目:点击打开题目链接 思路:暴力循环显然会超时,根据紫书提示,采取问题分解的方法,分成A+B与C+D,然后采取二分查找,复杂度降为O(n2logn) AC代码: #include <bits/ ...
- SDUST第十一次oj作业液晶显示问题
Problem H: 液晶显示 Time Limit: 1 Sec Memory Limit: 32 MBSubmit: 3246 Solved: 1594[Submit][Status][Web ...
- UVALive - 8273 Assigning Frequencies (搜索 )
n个点的一张图,问能否给每个点染上三种颜色中的一种,使得没有相邻的点颜色相同? n <= 35. Sample Input 4 6 6 0 3 1 5 3 2 2 5 0 4 1 0 7 12 ...