题目链接

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] (树链剖分,倍增,最小生成树)的更多相关文章

  1. BZOJ 2243: [SDOI2011]染色 树链剖分 倍增lca 线段树

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/pr ...

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

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

  3. BZOJ 3083: 遥远的国度 dfs序,树链剖分,倍增

    今天再做一天树的题目,明天要开始专攻图论了.做图论十几天之后再把字符串搞搞,区域赛前再把计几看看. 3083: 遥远的国度 Time Limit: 10 Sec  Memory Limit: 128 ...

  4. HDU3710 Battle over Cities(最小生成树+树链剖分+倍增+线段树)

    Battle over Cities Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Othe ...

  5. CF504E Misha and LCP on Tree 后缀自动机+树链剖分+倍增

    求树上两条路径的 LCP (树上每个节点代表一个字符) 总共写+调了6个多小时,终于过了~ 绝对是我写过的最复杂的数据结构了 我们对这棵树进行轻重链剖分,然后把所有的重链分正串,反串插入到广义后缀自动 ...

  6. 【树链剖分/倍增模板】【洛谷】3398:仓鼠找sugar

    P3398 仓鼠找sugar 题目描述 小仓鼠的和他的基(mei)友(zi)sugar住在地下洞穴中,每个节点的编号为1~n.地下洞穴是一个树形结构.这一天小仓鼠打算从从他的卧室(a)到餐厅(b),而 ...

  7. BZOJ 3083 树链剖分+倍增+线段树

    思路: 先随便选个点 链剖+线段树 1操作 就直接改root变量的值 2操作 线段树上改 3操作 分成三种情况 1.new root = xx 整个子树的min就是ans 2. lca(new roo ...

  8. Educational Codeforces Round 3 E. Minimum spanning tree for each edge (最小生成树+树链剖分)

    题目链接:http://codeforces.com/contest/609/problem/E 给你n个点,m条边. 问枚举每条边,问你加这条边的前提下组成生成树的权值最小的树的权值和是多少. 先求 ...

  9. BZOJ 1977: [BeiJing2010组队]次小生成树 Tree( MST + 树链剖分 + RMQ )

    做一次MST, 枚举不在最小生成树上的每一条边(u,v), 然后加上这条边, 删掉(u,v)上的最大边(或严格次大边), 更新答案. 树链剖分然后ST维护最大值和严格次大值..倍增也是可以的... - ...

随机推荐

  1. python_96_类的继承1

    #面向对象3大特性:封装,多态,继承 # 继承可节省内存,减少代码 class People(): def __init__(self,name,age): self.Name=name self.A ...

  2. no pointer in java

    Why there are no pointers in Java? In Java there are references instead of pointers. These reference ...

  3. NIOP 膜你题

    NOIp膜你题   Day1 duliu 出题人:ZAY    1.大美江湖(mzq.cpp/c) [题目背景] 细雪飘落长街,枫叶红透又一年不只为故友流连,其实我也恋长安听门外足音慢,依稀见旧时容颜 ...

  4. RestSharp使用备忘

    (1)一般调用: public static List<T> Execute<T>(string resourceUrl, object obj, out int totalN ...

  5. LINQ中AsEnumerable与AsQueryable的区别

    AsEnumerable将一个序列向上转换为一个IEnumerable, 强制将Enumerable类下面的查询操作符绑定到后续的子查询当中:AsQueryable将一个序列向下转换为一个IQuery ...

  6. oracle centos 重启后报错ORA-12514, TNS:listener does not currently know of service requested in connect descriptor

    oracle centos 重启后报错ORA-12514, TNS:listener does not currently know of service requested in connect d ...

  7. SAP HANA

    DROP PROCEDURE ""."ZCONCAT_EKKO_EBN"; CREATE PROCEDURE ""."ZCONCA ...

  8. hash 哈希查找复杂度为什么这么低?

    hash 哈希查找复杂度为什么这么低? (2017-06-23 21:20:36) 转载▼   分类: c from: 作者:jillzhang 出处:http://jillzhang.cnblogs ...

  9. svn提交报错,提示:locked,需要cleanup

    版权声明:本文为博主原创文章,未经博主允许不得转载. 原文地址: https://www.cnblogs.com/poterliu/p/9285137.html 在使用SVN提交代码或更新代码时经常会 ...

  10. Library setup