1977: [BeiJing2010组队]次小生成树 Tree

https://lydsy.com/JudgeOnline/problem.php?id=1977

题意:

  求严格次小生成树,即边权和不能等于最小生成树。

分析:

  倍增:求出最小生成树,然后枚举非树边,加入一条非树边,删掉环上的最大的边,如果最大的边等于加入的边,那么删掉环上次小的边。

  LCT:直接维护链上最大值,与次大值。

代码:

倍增

 #include<bits/stdc++.h>
using namespace std;
typedef long long LL; inline int read() {
int x=,f=;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-;
for (;isdigit(ch);ch=getchar())x=x*+ch-'';return x*f;
} const int N = ;
const int M = ;
const LL INF = 1e18; struct Edge{
int u,v,w;
bool operator < (const Edge &A) const {
return w < A.w;
}
}e[M];
int head[N],nxt[N<<],to[N<<],Enum;
int fa[N],deth[N],f[N][];
LL g1[N][],g2[N][],val[N<<],Sum;
bool used[M]; //------ used[N] inline void add_edge(int u,int v,int w) {
++Enum; to[Enum] = v; val[Enum] = w; nxt[Enum] = head[u]; head[u] = Enum;
++Enum; to[Enum] = u; val[Enum] = w; nxt[Enum] = head[v]; head[v] = Enum;
}
void dfs(int u,int fa) {
deth[u] = deth[fa] + ;
for (int i=head[u]; i; i=nxt[i]) {
int v = to[i];
if (v == fa) continue; // 写在下面了。。。
f[v][] = u;
g1[v][] = val[i]; // -- e[i].w
g2[v][] = -INF;
dfs(v,u);
}
}
inline void get(LL a,LL b,LL &mx1,LL &mx2) {
if (a > mx1) mx2 = max(mx1,b);
else if (a == mx1) mx2 = max(mx2, b);
else if (a < mx1) mx2 = max(mx2, a);
mx1 = max(mx1,a);
}
inline LL solve(int u,int v,int w) {
if (deth[u] < deth[v]) swap(u,v);
int d = deth[u] - deth[v];
LL mx1 = -INF, mx2 = -INF;
for (int i=; i>=; --i) {
if (d & ( << i)) {
get(g1[u][i],g2[u][i],mx1,mx2);
u = f[u][i];
}
}
if (u == v) return Sum + w - (mx1 < w ? mx1 : mx2);
for (int i=; i>=; --i) {
if (f[u][i] != f[v][i]) { // -- f[u][i]==f[u][i]
get(g1[u][i],g2[u][i],mx1,mx2);
get(g1[v][i],g2[v][i],mx1,mx2);
u = f[u][i], v = f[v][i];
}
}
get(g1[u][],g2[u][],mx1,mx2);
get(g1[v][],g2[v][],mx1,mx2);
return Sum + w - (mx1 < w ? mx1 : mx2);
}
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
int main() { int n = read(),m = read();
for (int i=; i<=m; ++i) {
e[i].u = read(), e[i].v = read(), e[i].w = read();
}
sort(e+,e+m+);
for (int i=; i<=n; ++i) fa[i] = i;
int cnt = ;
for (int i=; i<=m; ++i) {
int u = find(e[i].u), v = find(e[i].v);
if (u != v) {
used[i] = true;
add_edge(e[i].u,e[i].v,e[i].w);
fa[u] = v;
Sum += e[i].w;
cnt++;
if (cnt == n-) break;
}
}
dfs(,);
for (int j=; j<=; ++j)
for (int i=; i<=n; ++i) {
int k = f[i][j-];
LL a1 = g1[i][j-], a2 = g1[k][j-];
LL b1 = g2[i][j-], b2 = g2[k][j-];
f[i][j] = f[k][j-];
g1[i][j] = max(a1, a2);
if (a1 == a2) g2[i][j] = max(b1, b2);
else {
g2[i][j] = min(a1, a2);
g2[i][j] = max(g2[i][j], max(b1, b2)); //---
}
}
LL Ans = INF; // -- ANs = Sum
for (int i=; i<=m; ++i) {
if (!used[i])
Ans = min(Ans, solve(e[i].u,e[i].v,e[i].w));
}
cout << Ans;
return ;
}

LCT

 #include<bits/stdc++.h>
using namespace std;
typedef long long LL; inline int read() {
int x=,f=;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-;
for (;isdigit(ch);ch=getchar())x=x*+ch-'';return x*f;
} const int N = ;
const int M = ; struct Edge{
int u,v,w;
bool operator < (const Edge &A) const {
return w < A.w;
}
}e[M];
int fa[N<<],ch[N<<][],fir[N<<],sec[N<<],rev[N<<],sk[N<<],Top;
LL val[N<<];
int p[N]; bool isroot(int x) {
return ch[fa[x]][] != x && ch[fa[x]][] != x;
}
int son(int x) {
return ch[fa[x]][] == x;
}
void pushup(int x) {
int lc = ch[x][], rc = ch[x][];
if (fir[lc] > fir[rc]) fir[x] = fir[lc], sec[x] = max(sec[lc], fir[rc]);
else if (fir[lc] < fir[rc]) fir[x] = fir[rc], sec[x] = max(sec[rc], fir[lc]);
else fir[x] = fir[lc], sec[x] = max(sec[lc], sec[rc]); if (val[x] > fir[x]) sec[x] = fir[x], fir[x] = val[x];
else if (val[x] < fir[x] && val[x] > sec[x]) sec[x] = val[x];
}
void pushdown(int x) {
if (rev[x]) {
rev[ch[x][]] ^= , rev[ch[x][]] ^= ;
swap(ch[x][], ch[x][]);
rev[x] ^= ;
}
}
void rotate(int x) {
int y = fa[x],z = fa[y],b = son(x),c = son(y),a = ch[x][!b];
if (!isroot(y)) ch[z][c] = x; fa[x] = z;
ch[x][!b] = y;fa[y] = x;
ch[y][b] = a; if (a) fa[a] = y;
pushup(y); pushup(x);
}
void splay(int x) {
sk[Top = ] = x;
for (int i=x; !isroot(i); i=fa[i]) sk[++Top] = fa[i];
while (Top) pushdown(sk[Top--]);
while (!isroot(x)) {
int y = fa[x];
if (isroot(y)) rotate(x);
else {
if (son(x) == son(y)) rotate(y), rotate(x);
else rotate(x), rotate(x);
}
}
}
void access(int x) {
for (int last=; x; last=x,x=fa[x]) {
splay(x); ch[x][] = last; pushup(x);
}
}
void makeroot(int x) {
access(x); splay(x); rev[x] ^= ;
}
int find(int x) {
return x == p[x] ? x : p[x] = find(p[x]);
} int main() {
int n = read(),m = read(),Index = n;
for (int i=; i<=n; ++i) p[i] = i;
for (int i=; i<=m; ++i) {
e[i].u = read(), e[i].v = read(), e[i].w = read();
}
sort(e+,e+m+);
LL Sum = , Ans = 1e18;
for (int i=; i<=m; ++i) {
int x = e[i].u, y = e[i].v;
int u = find(x), v = find(y);
if (u != v) {
makeroot(x);
fa[x] = ++Index; // 化边权为点权
fa[Index] = y;
fir[Index] = val[Index] = e[i].w;
Sum += e[i].w;
p[u] = v;
}
else {
makeroot(x);
access(y);
splay(y);
Ans = min(Ans, (LL)e[i].w - (e[i].w > fir[y] ? fir[y] : sec[y]));
}
}
cout << Ans + Sum;
return ;
}

upd:2018.10.22

前几天模拟赛出了这道题,考试的时候写的,感觉比以前的好写些。

 #include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#include<cctype>
#include<set>
#include<vector>
#include<queue>
#include<map>
#define fi(s) freopen(s,"r",stdin);
#define fo(s) freopen(s,"w",stdout);
using namespace std;
typedef long long LL; char buf[], *p1 = buf, *p2 = buf;
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
inline int read() {
int x=,f=;char ch=nc();for(;!isdigit(ch);ch=nc())if(ch=='-')f=-;
for(;isdigit(ch);ch=nc())x=x*+ch-'';return x*f;
} const int N = ;
const int LOG = ; struct Edge{
int u, v; LL w;
bool operator < (const Edge &A) const {
return w < A.w;
}
}e[N * ], R[N * ]; struct ST{
int x;LL mx, ci;
ST() { x = mx = ci = ; }
}f[N][];
int head[N], to[N * ], nxt[N * ]; LL len[N * ];
int fa[N], deth[N];
int n, m, En, tot; LL Sum; ST operator + (const ST &A, const ST &B) {
ST res; res.x = B.x;
if (A.mx > B.mx) res.mx = A.mx, res.ci = max(A.ci, B.mx);
else if (B.mx > A.mx) res.mx = B.mx, res.ci = max(A.mx, B.ci);
else res.mx = A.mx, res.ci = max(A.ci, B.ci);
return res;
} inline void add_edge(int u,int v,LL w) {
++En; to[En] = v; len[En] = w; nxt[En] = head[u]; head[u] = En;
++En; to[En] = u; len[En] = w; nxt[En] = head[v]; head[v] = En;
}
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void Kruskal() {
sort(e + , e + m + );
for (int i=; i<=n; ++i) fa[i] = i;
for (int i=; i<=m; ++i) {
int u = find(e[i].u), v = find(e[i].v);
if (u != v) {
fa[u] = v; Sum += e[i].w;
add_edge(e[i].u, e[i].v, e[i].w);
}
else R[++tot] = e[i];
}
} void dfs(int u,int pa) {
deth[u] = deth[pa] + ;
for (int i=head[u]; i; i=nxt[i]) {
int v = to[i];
if (v == pa) continue;
dfs(v, u);
f[v][].x = u;
f[v][].mx = len[i];
}
} void D(const ST &A) {
cerr << A.x << " " << A.mx << " " << A.ci << "\n";
} ST work(int u,int v) {
ST ans; //ans.mx = -1; ans.ci = -1; ans.x = 0;
if (deth[u] < deth[v]) swap(u, v);
int d = deth[u] - deth[v];
for (int j=LOG; j>=; --j) {
if ((d >> j) & ) {
ans = ans + f[u][j]; // 顺序!!!
u = f[u][j].x;
// D(ans);
}
}
if (u == v) return ans;
for (int j=LOG; j>=; --j) {
if (f[u][j].x != f[v][j].x) {
ans = ans + f[u][j]; ans = ans + f[v][j];
u = f[u][j].x, v = f[v][j].x;
// D(ans);
}
}
return ans + f[u][] + f[v][];
} int main() { //fi("2.txt"); // freopen("tree.in","r",stdin);
// freopen("tree.out","w",stdout); n = read(), m = read();
for (int i=; i<=m; ++i)
e[i].u = read(), e[i].v = read(), e[i].w = read();
sort(e + , e + m + );
Kruskal();
dfs(, ); for (int j=; j<=LOG; ++j)
for (int i=; i<=n; ++i)
f[i][j] = f[i][j - ] + f[f[i][j - ].x][j - ]; ST now;
LL Ans = 1e18;
for (int i=; i<=tot; ++i) {
now = work(R[i].u, R[i].v);
if (R[i].w != now.mx) Ans = min(Ans, Sum - now.mx + R[i].w);
else if (now.ci) Ans = min(Ans, Sum - now.ci + R[i].w);
}
cout << Ans;
return ;
}

1977: [BeiJing2010组队]次小生成树 Tree的更多相关文章

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

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

  2. BZOJ 1977[BeiJing2010组队]次小生成树 Tree - 生成树

    描述: 就是求一个次小生成树的边权和 传送门 题解 我们先构造一个最小生成树, 把树上的边记录下来. 然后再枚举每条非树边(u, v, val),在树上找出u 到v 路径上的最小边$g_0$ 和 严格 ...

  3. 【刷题】BZOJ 1977 [BeiJing2010组队]次小生成树 Tree

    Description 小 C 最近学了很多最小生成树的算法,Prim 算法.Kurskal 算法.消圈算法等等. 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了.小 P 说,让小 C 求出一 ...

  4. bzoj 1977 [BeiJing2010组队]次小生成树 Tree

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1977 kruscal别忘了先按边权sort.自己觉得那部分处理得还挺好的.(联想到之前某题的 ...

  5. BZOJ 1977: [BeiJing2010组队]次小生成树 Tree 倍增 最小生成树

    好吧我太菜了又调了一晚上...QAQ 先跑出最小生成树,标记树边,再用树上倍增的思路,预处理出: f[u][i] :距离u为2^i的祖先 h[u][i][0/1] :距u点在2^i范围内的最长边和次长 ...

  6. [BeiJing2010组队]次小生成树 Tree

    1977: [BeiJing2010组队]次小生成树 Tree Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 5168  Solved: 1668[S ...

  7. 【BZOJ1977】[BeiJing2010组队]次小生成树 Tree 最小生成树+倍增

    [BZOJ1977][BeiJing2010组队]次小生成树 Tree Description 小 C 最近学了很多最小生成树的算法,Prim 算法.Kurskal 算法.消圈算法等等. 正当小 C ...

  8. 洛谷P4180 [Beijing2010组队]次小生成树Tree(最小生成树,LCT,主席树,倍增LCA,倍增,树链剖分)

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

  9. 【次小生成树】bzoj1977 [BeiJing2010组队]次小生成树 Tree

    Description 小 C 最近学了很多最小生成树的算法,Prim 算法.Kurskal 算法.消圈算法等等. 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了.小 P 说,让小 C 求出一 ...

随机推荐

  1. 【转载】#458 Errors While Converting Between enum and Underlying Type

    You can convert to an enum value from its underlying type by casting the underlying type (e.g. int) ...

  2. Linux下elk安装配置

    安装jdkJDK版本大于1.8 elk下载地址:https://www.elastic.co/products注意:elk三个版本都要保持一致. rpm -ivh elasticsearch-5.4. ...

  3. Android(java)学习笔记22:我们到底该如何处理异常?

    1. 我们到底该如何处理异常? (1)原则: 如果该功能内部可以将问题处理,用try,自己能解决就自己解决问题. 如果处理不了,交由调用者处理,这是用throws,自己不能解决的问题,我们就抛出去交个 ...

  4. SQL SERVER 2012断日志

    有一个SQL2012库的日志达到了100G左右,平时开发人员根本没有做过事务日志备份,而磁盘空间已经快满了.所以,只能截断它.但是,由于从2K8以后,SQL SERVER好像不再提供 truncate ...

  5. MyBatis的settings设置描述

    settings 中的设置是非常关键的,它们会改变 MyBatis 的运行时行为.下表描述了设置中各项的意图.默认值等. 设置参数 描述 有效值 默认值 cacheEnabled 该配置影响的所有映射 ...

  6. Android学习笔记_49_Android中自定义属性(attrs.xml,TypedArray的使用)

    做Android布局是件很享受的事,这得益于他良好的xml方式.使用xml可以快速有效的为软件定义界面.可是有时候我们总感觉官方定义的一些基本组件不够用,自定义组件就不可避免了.那么如何才能做到像官方 ...

  7. c# 常见验证邮箱、电话号码、日期等格式

    #region 验证邮箱验证邮箱 /**//// <summary> /// 验证邮箱 /// </summary> /// <param name="sour ...

  8. vue-resource+iview上传文件取消上传

    vue-resource+iview上传文件取消上传 子组件: <template> <div class="upload-area-div"> <U ...

  9. 简析--UUID

    内容转载自:http://www.cnblogs.com/java-class/p/4727698.html 阅读目录 1.UUID简介 2.UUID组成 3.UUID实战演练 1.UUID 简介 U ...

  10. flask中的CBV和FBV

    flask中CBV使用 from flask import Flask, views app = Flask(__name__) class Login(views.MethodView): meth ...