BZOJ 1977: [BeiJing2010组队]次小生成树(Kruskal+树上倍增)


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

  Time Limit: 10 Sec
  Memory Limit: 512 MB

Description###

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

Input###

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

Output###

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

Sample Input 1###

  5 6
  1 2 1
  1 3 2
  2 4 3
  3 5 4
  3 4 3
  4 5 6
  

Sample Output 1###

  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 。
  

题目地址: BZOJ 1977: [BeiJing2010组队]次小生成树

题解:

     
  先求出最小生成树,然后枚举每条不在该树上的边  
  求出此边两点的树上路径中小于此边长度的最大的边(题目要求严格次小
  可用树上倍增解决,因为最大的边可能等于枚举的边,所以还要求出严格次大的边
  虽然很水但我还是被优先级和初值坑了三个小时QWQ
www.cnblogs.com/AGFghy/


AC代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
struct path
{
    int x,y,l;
}p[300005];
int n,m,num,f1,f2;
ll now,sum,n1,n2,ans;
int point[200005],len[200005],Next[200005],head[100005],fa[100005],dep[100005],mark[100005],f[100005][25];
ll mx1[100005][25],mx2[100005][25];
bool cmp(path p1,path p2)
{
    return p1.l<p2.l;
}
int find(int k)
{
    if (k!=fa[k]) return fa[k]=find(fa[k]);
    return k;
}
void add(int u,int v,int nl)
{
    num++;
    point[num]=v;
    len[num]=nl;
    Next[num]=head[u];
    head[u]=num;
}
void find(int now,int pre)
{
    dep[now]=dep[pre]+1;
    f[now][0]=pre;
    for (int i=head[now]; i!=-1; i=Next[i])
    {
        int v=point[i];
        if (v!=pre) 
        {
            mx1[v][0]=len[i];
            find(v,now);
        }
    }
}
ll max(ll x,ll y)
{
    if (x>y) return x;
    return y;
}
ll min(ll x,ll y)
{
    if (x<y) return x;
    return y;
}
void lca(int u,int v)
{
    if (dep[u]<dep[v]) swap(u,v);
    int deep=dep[u]-dep[v];
    for (int i=0; i<=20; i++)
        if ((deep&(1<<i))>0) 
        {
            n2=max(n2,mx2[u][i]);
            if (n1!=mx1[u][i]) n2=max(n2,min(n1,mx1[u][i]));
            n1=max(n1,mx1[u][i]);
            u=f[u][i];
        }
    for (int i=20; i>=0; i--)
        if (f[u][i]!=f[v][i])
        {
            n2=max(n2,mx2[u][i]);
            if (n1!=mx1[u][i]) n2=max(n2,min(n1,mx1[u][i]));
            n1=max(n1,mx1[u][i]);
            u=f[u][i];
              
            n2=max(n2,mx2[v][i]);
            if (n1!=mx1[v][i]) n2=max(n2,min(n1,mx1[v][i]));
            n1=max(n1,mx1[v][i]);
            v=f[v][i];
        }
    if (u!=v)
    {
        n2=max(n2,mx2[u][0]);
        if (n1!=mx1[u][0]) n2=max(n2,min(n1,mx1[u][0]));
        n1=max(n1,mx1[u][0]);
          
        n2=max(n2,mx2[v][0]);
        if (n1!=mx1[v][0]) n2=max(n2,min(n1,mx1[v][0]));
        n1=max(n1,mx1[v][0]);
    } 
    return;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1; i<=n; i++)
        head[i]=-1;
    for (int i=1; i<=m; i++)
        scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].l);
         
    sort(p+1,p+m+1,cmp);
    for (int i=1; i<=n; i++)
        fa[i]=i;
    now=n-1;
    for (int i=1; i<=m; i++)
    {
        f1=find(p[i].x);
        f2=find(p[i].y);
        if (f1!=f2)
        {
            now--;
            fa[f2]=f1;
            sum+=p[i].l;
            mark[i]=1;
            add(p[i].x,p[i].y,p[i].l);
            add(p[i].y,p[i].x,p[i].l);
            if (now==0) break;
        }
    }
    memset(mx1,-1,sizeof(mx1));
    memset(mx2,-1,sizeof(mx2));
    find(1,0);
    for (int j=1; j<=20; j++)
        for (int i=1; i<=n; i++)
        if (f[i][j-1])
        {
            f[i][j]=f[f[i][j-1]][j-1];
            mx1[i][j]=max(mx1[i][j-1],mx1[f[i][j-1]][j-1]);
            if (mx1[i][j-1]!=mx1[f[i][j-1]][j-1]) mx2[i][j]=min(mx1[i][j-1],mx1[f[i][j-1]][j-1]);
            mx2[i][j]=max(mx2[i][j],max(mx2[i][j-1],mx2[f[i][j-1]][j-1]));
        }
    ans=1ll<<50;
    for (int i=1; i<=m; i++)
        if (mark[i]==0)
        {
            n1=-1; n2=-1;
            lca(p[i].x,p[i].y);
            if (n1!=p[i].l) 
            {
                now=sum+p[i].l-n1;
                ans=min(ans,now);
            }
            else if (n2!=-1)
            {
                now=sum+p[i].l-n2;
                ans=min(ans,now);
            }
        }
    printf("%lld\n",ans);
    return 0;
}
posted @ 2018-07-19 18:40  AGFghy  阅读(173)  评论(0编辑  收藏  举报