2013-team4/code/submst
从 Trac 迁移的文章
这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。
原文章内容如下:
{{{
严格次小:次小生成树权值和最小生成树不同
非严格次小:次小生成树权值和最小生成树可以相同
方法一:时间复杂度O(N^3),不推荐使用
1. 求出图G的最小生成树T;
2. 枚举T上的每一条边将其从G中删去,再求一次最小生成树,并记录权值(要求当前权值ω(T')与ω(T)不同);
3. 步骤2中权值的最小值就是所求的次小生成树.
方法二:时间复杂度O(N^2)或者O(MlogM),推荐使用,稀疏图需用O(MlogM)的,稠密图需用O(N^2)
1. 求出图G的最小生成树T;
2. 求出T中任意两个结点u和v的路径中最长边和次长边(要求两者不相同,这里的目的是有些题目要求严格次小);
3. 枚举G中不在T上的每一条边<u,v>,用<u,v>替换u和v的路径中最长边(如果最长边和<u,v>权值相同,那么替换次长边, 目的同上).
}}}
{{{
//N^2版本,严格次小,实现简单
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
const int MAXN=1005;
const int inf=1000000000;
vector<int> G[MAXN];
int val[MAXN][MAXN];
int map[MAXN][MAXN];
int pre[MAXN];
int vis[MAXN];
int N;
int prim(int n, int mat[][MAXN], int *pre) {
int Min[MAXN], ret=0;
int i, j, k;
for (i=0; i<n; i++)
Min[i]=inf, vis[i]=false, pre[i]=-1;
for (Min[j=0]=0; j<n; j++) {
for (k=-1, i=0; i<n; i++)
if (!vis[i]&&(k==-1||Min[i]<Min[k]))
k=i;
for (vis[k]=1, ret+=Min[k], i=0; i<n; i++)
if (!vis[i]&&mat[k][i]<Min[i])
Min[i]=mat[pre[i]=k][i];
}
return ret;
}
void dfs(int id, int x, int v) {
vis[x]=true; val[id][x]=v;
for (int i=0; i<G[x].size(); i++) {
int y=G[x][i]; if (vis[y]) continue;
int tmp=v;
if (tmp<map[x][y]) tmp=map[x][y];
dfs(id, y, tmp);
}
}
int solve()
int ret=prim(N, map, pre);
for (int i=0; i<N; i++) G[i].clear();
for (int i=0; i<N; i++)
if (pre[i]!=-1) {
G[i].push_back(pre[i]);
G[pre[i]].push_back(i);
}
int ans=inf;
for (int i=0; i<N; i++) {
for (int j=0; j<N; j++) vis[j]=false;
dfs(i, i, -1);
for (int j=i+1; j<N; j++)
{
int tmp=ret-value[i][j]+map[i][j];
if (tmp!=ret&&tmp<ans) ans=tmp;
}
return ans;
}
}}}
{{{
//MlogM版本,这里要求严格次小,实现比较复杂,需要求LCA
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN=100000+10;
const int MAXM=300000+10;
const int LOG=22;
struct node {
int u, v, w;
};
node edge[MAXM];
int belong[MAXN];
int head[MAXN], next[MAXM], adj[MAXM], flow[MAXM];
int anc[LOG][MAXN], dp[2][LOG][MAXN];
int dep[MAXN], fa[MAXN];
bool used[MAXM];
int N, M;
LL ret;
bool cmp(const node &a, const node &b) {
return (a.w<b.w);
}
int getf(int x) {
if (x!=belong[x]) belong[x]=getf(belong[x]);
return belong[x];
}
void build() {
int cnt=0, times=0;
for (int i=1; i<=N; i++) head[i]=-1, belong[i]=i;
for (int i=0; i<M; i++) {
int u=edge[i].u, v=edge[i].v, w=edge[i].w;
int fu=getf(u), fv=getf(v); used[i]=false;
if (fu!=fv) {
belong[fu]=fv; ret+=w; times++; used[i]=true;
adj[cnt]=v, flow[cnt]=w, next[cnt]=head[u], head[u]=cnt++;
adj[cnt]=u, flow[cnt]=w, next[cnt]=head[v], head[v]=cnt++;
if (times==N-1) return;
}
}
}
void dfs(int u) {
for (int now=head[u]; now!=-1; now=next[now]) {
int v=adj[now], w=flow[now];
if (dep[v]==-1) {
dep[v]=dep[u]+1;
anc[0][v]=u; dp[0][0][v]=w;
dfs(v);
}
}
}
void get_anc() {
for (int i=1; (1<<i)<=N; i++)
for (int j=1; j<=N; j++)
if (anc[i-1][j]!=-1) {
anc[i][j]=anc[i-1][anc[i-1][j]];
if (anc[i][j]!=-1) dp[0][i][j]=max(dp[0][i-1][j], dp[0][i-1][anc[i-1][j]]);
if (i==1&&dp[0][0][j]!=dp[0][0][anc[0][j]]) dp[1][1][j]=min(dp[0][0][j], dp[0][0][anc[0][j]]);
else if (anc[i][j]!=-1) {
dp[1][i][j]=max(dp[1][i-1][j], dp[1][i-1][anc[i-1][j]]);
if (dp[0][i-1][j]>dp[0][i-1][anc[i-1][j]]) dp[1][i][j]=max(dp[1][i][j], dp[0][i-1][anc[i-1][j]]);
if (dp[0][i-1][j]<dp[0][i-1][anc[i-1][j]]) dp[1][i][j]=max(dp[1][i][j], dp[0][i-1][j]);
}
}
}
inline int lca(int a, int b) {
if (dep[a]<dep[b]) swap(a, b);
int l=(int)(log(N)/log(2))+1;
for (int i=l; i>=0; i--)
if (dep[a]>=dep[b]+(1<<i)) {
a=anc[i][a];
if (a==b) return a;
}
for (int i=l; i>=0; i--)
if (anc[i][a]!=-1&&anc[i][a]!=anc[i][b]) {
a=anc[i][a], b=anc[i][b];
}
return anc[0][a];
}
inline int get_maxlen(int a, int b, int c) {
int res=-1, t=a;
int l=(int)(log(N)/log(2))+1;
for (int i=l; i>=0; i--)
if (dep[a]>=dep[b]+(1<<i)) {
res=max(res, dp[0][i][a]);
a=anc[i][a];
}
if (res!=c) return res;
res=-1; a=t;
for (int i=l; i>=0; i--)
if (dep[a]>=dep[b]+(1<<i)) {
res=max(res, dp[1][i][a]);
a=anc[i][a];
}
return res;
}
void solve() {
build();
for (int i=0; (1<<i)<=N; i++)
for (int j=0; j<=N; j++)
anc[i][j]=dp[0][i][j]=dp[1][i][j]=-1;
for (int i=0; i<=N; i++) dep[i]=-1;
dep[1]=0;
dfs(1);
get_anc();
LL ans=(1ll<<60ll);
for (int i=0; i<M; i++) {
if (used[i]) continue;
int u=edge[i].u, v=edge[i].v, w=edge[i].w;
int root=lca(u, v);
LL tmp=max(get_maxlen(u, root, w), get_maxlen(v, root, w));
if (tmp==-1) continue;
tmp=ret-tmp+w;
if (tmp<ans&&tmp!=ret) ans=tmp;
}
printf("%lld\n", ans);
}
int main() {
scanf("%d%d", &N, &M);
for (int i=0; i<M; i++) scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].w);
sort(edge, edge+M, cmp);
solve();
return 0;
}
}}}
严格次小:次小生成树权值和最小生成树不同
非严格次小:次小生成树权值和最小生成树可以相同
方法一:时间复杂度O(N^3),不推荐使用
1. 求出图G的最小生成树T;
2. 枚举T上的每一条边将其从G中删去,再求一次最小生成树,并记录权值(要求当前权值ω(T')与ω(T)不同);
3. 步骤2中权值的最小值就是所求的次小生成树.
方法二:时间复杂度O(N^2)或者O(MlogM),推荐使用,稀疏图需用O(MlogM)的,稠密图需用O(N^2)
1. 求出图G的最小生成树T;
2. 求出T中任意两个结点u和v的路径中最长边和次长边(要求两者不相同,这里的目的是有些题目要求严格次小);
3. 枚举G中不在T上的每一条边<u,v>,用<u,v>替换u和v的路径中最长边(如果最长边和<u,v>权值相同,那么替换次长边, 目的同上).
//N^2版本,严格次小,实现简单
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
const int MAXN=1005;
const int inf=1000000000;
vector<int> G[MAXN];
int val[MAXN][MAXN];
int map[MAXN][MAXN];
int pre[MAXN];
int vis[MAXN];
int N;
int prim(int n, int mat[][MAXN], int *pre) {
int Min[MAXN], ret=0;
int i, j, k;
for (i=0; i<n; i++)
Min[i]=inf, vis[i]=false, pre[i]=-1;
for (Min[j=0]=0; j<n; j++) {
for (k=-1, i=0; i<n; i++)
if (!vis[i]&&(k==-1||Min[i]<Min[k]))
k=i;
for (vis[k]=1, ret+=Min[k], i=0; i<n; i++)
if (!vis[i]&&mat[k][i]<Min[i])
Min[i]=mat[pre[i]=k][i];
}
return ret;
}
void dfs(int id, int x, int v) {
vis[x]=true; val[id][x]=v;
for (int i=0; i<G[x].size(); i++) {
int y=G[x][i]; if (vis[y]) continue;
int tmp=v;
if (tmp<map[x][y]) tmp=map[x][y];
dfs(id, y, tmp);
}
}
int solve()
int ret=prim(N, map, pre);
for (int i=0; i<N; i++) G[i].clear();
for (int i=0; i<N; i++)
if (pre[i]!=-1) {
G[i].push_back(pre[i]);
G[pre[i]].push_back(i);
}
int ans=inf;
for (int i=0; i<N; i++) {
for (int j=0; j<N; j++) vis[j]=false;
dfs(i, i, -1);
for (int j=i+1; j<N; j++)
{
int tmp=ret-value[i][j]+map[i][j];
if (tmp!=ret&&tmp<ans) ans=tmp;
}
return ans;
}
//MlogM版本,这里要求严格次小,实现比较复杂,需要求LCA
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN=100000+10;
const int MAXM=300000+10;
const int LOG=22;
struct node {
int u, v, w;
};
node edge[MAXM];
int belong[MAXN];
int head[MAXN], next[MAXM], adj[MAXM], flow[MAXM];
int anc[LOG][MAXN], dp[2][LOG][MAXN];
int dep[MAXN], fa[MAXN];
bool used[MAXM];
int N, M;
LL ret;
bool cmp(const node &a, const node &b) {
return (a.w<b.w);
}
int getf(int x) {
if (x!=belong[x]) belong[x]=getf(belong[x]);
return belong[x];
}
void build() {
int cnt=0, times=0;
for (int i=1; i<=N; i++) head[i]=-1, belong[i]=i;
for (int i=0; i<M; i++) {
int u=edge[i].u, v=edge[i].v, w=edge[i].w;
int fu=getf(u), fv=getf(v); used[i]=false;
if (fu!=fv) {
belong[fu]=fv; ret+=w; times++; used[i]=true;
adj[cnt]=v, flow[cnt]=w, next[cnt]=head[u], head[u]=cnt++;
adj[cnt]=u, flow[cnt]=w, next[cnt]=head[v], head[v]=cnt++;
if (times==N-1) return;
}
}
}
void dfs(int u) {
for (int now=head[u]; now!=-1; now=next[now]) {
int v=adj[now], w=flow[now];
if (dep[v]==-1) {
dep[v]=dep[u]+1;
anc[0][v]=u; dp[0][0][v]=w;
dfs(v);
}
}
}
void get_anc() {
for (int i=1; (1<<i)<=N; i++)
for (int j=1; j<=N; j++)
if (anc[i-1][j]!=-1) {
anc[i][j]=anc[i-1][anc[i-1][j]];
if (anc[i][j]!=-1) dp[0][i][j]=max(dp[0][i-1][j], dp[0][i-1][anc[i-1][j]]);
if (i==1&&dp[0][0][j]!=dp[0][0][anc[0][j]]) dp[1][1][j]=min(dp[0][0][j], dp[0][0][anc[0][j]]);
else if (anc[i][j]!=-1) {
dp[1][i][j]=max(dp[1][i-1][j], dp[1][i-1][anc[i-1][j]]);
if (dp[0][i-1][j]>dp[0][i-1][anc[i-1][j]]) dp[1][i][j]=max(dp[1][i][j], dp[0][i-1][anc[i-1][j]]);
if (dp[0][i-1][j]<dp[0][i-1][anc[i-1][j]]) dp[1][i][j]=max(dp[1][i][j], dp[0][i-1][j]);
}
}
}
inline int lca(int a, int b) {
if (dep[a]<dep[b]) swap(a, b);
int l=(int)(log(N)/log(2))+1;
for (int i=l; i>=0; i--)
if (dep[a]>=dep[b]+(1<<i)) {
a=anc[i][a];
if (a==b) return a;
}
for (int i=l; i>=0; i--)
if (anc[i][a]!=-1&&anc[i][a]!=anc[i][b]) {
a=anc[i][a], b=anc[i][b];
}
return anc[0][a];
}
inline int get_maxlen(int a, int b, int c) {
int res=-1, t=a;
int l=(int)(log(N)/log(2))+1;
for (int i=l; i>=0; i--)
if (dep[a]>=dep[b]+(1<<i)) {
res=max(res, dp[0][i][a]);
a=anc[i][a];
}
if (res!=c) return res;
res=-1; a=t;
for (int i=l; i>=0; i--)
if (dep[a]>=dep[b]+(1<<i)) {
res=max(res, dp[1][i][a]);
a=anc[i][a];
}
return res;
}
void solve() {
build();
for (int i=0; (1<<i)<=N; i++)
for (int j=0; j<=N; j++)
anc[i][j]=dp[0][i][j]=dp[1][i][j]=-1;
for (int i=0; i<=N; i++) dep[i]=-1;
dep[1]=0;
dfs(1);
get_anc();
LL ans=(1ll<<60ll);
for (int i=0; i<M; i++) {
if (used[i]) continue;
int u=edge[i].u, v=edge[i].v, w=edge[i].w;
int root=lca(u, v);
LL tmp=max(get_maxlen(u, root, w), get_maxlen(v, root, w));
if (tmp==-1) continue;
tmp=ret-tmp+w;
if (tmp<ans&&tmp!=ret) ans=tmp;
}
printf("%lld\n", ans);
}
int main() {
scanf("%d%d", &N, &M);
for (int i=0; i<M; i++) scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].w);
sort(edge, edge+M, cmp);
solve();
return 0;
}