bzoj 1977 [BeiJing2010组队]次小生成树 Tree [严格的次小生成树]

19 篇文章 0 订阅
5 篇文章 0 订阅

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

Time Limit: 10 Sec Memory Limit: 512 MB
Submit: 2777 Solved: 695

Description

小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e的权值) 这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。

Input

第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。

Output

包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)

Sample Input

5 6

1 2 1

1 3 2

2 4 3

3 5 4

3 4 3

4 5 6

Sample Output
11

HINT

数据中无向图无自环; 50% 的数据N≤2 000 M≤3 000; 80% 的数据N≤50 000 M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。


这道题要求严格的次小MST,思路差不多,但是不能只记录max,还要记录次大值max2,更新的时候分情况讨论,具体看代码。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef long long LL;
const LL INF=(1ll<<60);
const int maxn = 100005;
const int maxm = 300005;
struct Edge
{
    int to,next;
    int val;
}edge[maxn<<1]; // set for the MST
int head[maxn];
int maxedge;
inline void addedge(int u,int v,int c)
{
    edge[++maxedge] = (Edge) { v,head[u],c };
    head[u] = maxedge;
    edge[++maxedge] = (Edge) { u,head[v],c };
    head[v] = maxedge;
}
struct Road
{
    int u,v;
    int c;
    bool operator < (const Road t) const
    {
        return c < t.c;
    }
}road[maxm];
bool used[maxm];
int n,m;
LL MST;
int fa[maxn];
int find(int x) { return fa[x]^x? fa[x]=find(fa[x]) : x ; }
inline bool union_find(int x,int y)
{
    int t1=find(x),t2=find(y);
    if(t1==t2) return false;
    fa[t2]=t1;
    return true;
}
LL kruskal()
{
    int tot = 0;
    LL ans = 0ll;
    for(int i=1;i<=m;i++)
    {
        if(union_find(road[i].u,road[i].v)) tot++,ans+=road[i].c,addedge(road[i].u,road[i].v,road[i].c),used[i]=true;
        if(tot==n-1) break;
    }
    return ans;
}
#define maxd 25
#define D 20
int f[maxn][maxd];
int dp[maxn][maxd][2];
int depth[maxn];
void dfs(int u,int father,int dep)
{
    depth[u] = dep;
    for(int k=1;k<=D;k++)
    {
        f[u][k] = f[f[u][k-1]][k-1];
        dp[u][k][0] = max(dp[u][k-1][0],dp[f[u][k-1]][k-1][0]);
        if(dp[u][k-1][0] == dp[f[u][k-1]][k-1][0]) dp[u][k][1] = max(dp[u][k-1][1],dp[f[u][k-1]][k-1][1]);
        else
        {
            dp[u][k][1] = min(dp[u][k-1][0],dp[f[u][k-1]][k-1][0]);
            smax(dp[u][k][1] , max(dp[u][k-1][1],dp[f[u][k-1]][k-1][1]));
        }
    }
    for(int i=head[u];~i;i=edge[i].next)
    {
        int v = edge[i].to;
        if(v==father) continue;
        f[v][0] = u;
        dp[v][0][0] = (LL)edge[i].val;
        // provided two number differs!
        dfs(v,u,dep+1);
    }
}
inline void merge(int &ans1,int &ans2,int u,int k)
{
    int t1 = max(ans1,dp[u][k][0]) , t2;
    if(ans1 == dp[u][k][0]) t2 = max(ans2,dp[u][k][1]);
    else
    {
        t2 = min(ans1,dp[u][k][0]);
        smax(t2,max(ans2,dp[u][k][1]));
    }
    ans1 = t1;
    ans2 = t2;
}
pair <int,int> query(int u,int v)
{
    int ans1 = 0;
    int ans2 = 0;
    if(depth[u]<depth[v]) swap(u,v);
    for(int k=D;k>=0;k--) if(depth[f[u][k]]>=depth[v])
    {
        merge(ans1,ans2,u,k);
        u = f[u][k];
    }
    if(u==v) return make_pair(ans1,ans2);
    for(int k=D;k>=0;k--) if(f[u][k] ^ f[v][k])
    {
        merge(ans1,ans2,u,k); u=f[u][k];
        merge(ans1,ans2,v,k); v=f[v][k];
    }
    merge(ans1,ans2,u,0); merge(ans1,ans2,v,0);
    return make_pair(ans1,ans2);
}
void init()
{
    scanf("%d%d",&n,&m);
    memset(head,-1,sizeof(head));
    maxedge=-1;
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=m;i++) scanf("%d%d%d",&road[i].u,&road[i].v,&road[i].c);
    sort(road+1,road+m+1);
    MST=kruskal();
    dfs(1,-1,0);
}
LL work()
{
    LL subMST=INF;
    for(int i=1;i<=m;i++) if(!used[i])
    {
        pair <int,int> tmp = query(road[i].u,road[i].v);
        if(road[i].c ^ tmp.first) smin(subMST,MST - tmp.first + road[i].c);
        else smin(subMST,MST - tmp.second + road[i].c);
    }
    return subMST;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("unique.in","r",stdin);
    freopen("unique.out","w",stdout);
    #endif
    init();
    printf(AUTO,work());
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值