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

题意:求严格次小生成树,保证存在。

首先求出原图的最小生成树,我们先考虑次小生成树是怎么得来的,加入一条非树边后原先的树中就会出现一个环,用加入的那条边替换掉环上最大的树边,但这题并不能这么做,因为会出现环上最大的边=非树边的情况,所以我们既要找出环上的最大边,又要找出严格次大边,这个可以用树上倍增解决,因为我比较傻叉,所以写了个link-cut tree,勿喷…(其实LCT也不是很慢)
Tips:找严格次大边要注意,我WA了一发,还有开longlong

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
using namespace std;
const int maxn=400000+10;
const int INF=1000000000;
int c[maxn][2],fa[maxn],v[maxn],mn[maxn],rev[maxn],st[maxn],tot,pa[maxn],n,m,flag[maxn],mn2[maxn];
long long ans=0,out=(long long)1000000000*(long long)1000000000;
struct edge
{
  int u,v,c;
}e[maxn];
int cmp(edge a,edge b)
{
  return a.c<b.c;
}
int find(int p)
{
  if(pa[p]==p) return p;
  else return pa[p]=find(pa[p]);
}
inline bool isroot(int x)
{
  if(c[fa[x]][1]!=x&&c[fa[x]][0]!=x) return true;
  else return false;
}
void pushup(int x)
{
  int l=c[x][0],r=c[x][1];
  mn[x]=v[x];mn2[x]=0;
  if(l) 
  {
    if(mn[l]>mn[x]) 
    {
      mn2[x]=max(mn2[l],mn[x]);
      mn[x]=mn[l];
    }
    else if(mn[l]>mn2[x]) 
    {
      if(mn[l]<mn[x]) mn2[x]=mn[l];
      else mn2[x]=max(mn2[x],mn2[l]);
    }
  }
  if(r) 
  {
    if(mn[r]>mn[x])
    {
      mn2[x]=max(mn2[r],mn[x]);
      mn[x]=mn[r];
    }
    else if(mn[r]>mn2[x]) 
    {
      if(mn[r]<mn[x]) mn2[x]=mn[r];
      else mn2[x]=max(mn2[x],mn2[r]);
    }
  }
}
void pushdown(int x)
{
  int l=c[x][0],r=c[x][1];
  if(rev[x])
  {
    rev[x]^=1;rev[l]^=1;rev[r]^=1;
    swap(c[x][0],c[x][1]);
  }
}
void rotate(int x)
{
  int y=fa[x],z=fa[y],l,r;
  if(c[y][0]==x) l=0;else l=1;r=l^1;
  if(!isroot(y))
  {
    if(c[z][0]==y) c[z][0]=x;
    else c[z][1]=x;
  }
  fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
  c[y][l]=c[x][r];c[x][r]=y;pushup(y);pushup(x);
}
void splay(int x)
{
  int top=0;st[top=1]=x;
  for(int i=x;!isroot(i);i=fa[i])
    st[++top]=fa[i];
  for(int i=top;i;i--) pushdown(st[i]);
  while(!isroot(x))
  {
    int y=fa[x],z=fa[y];
    if(!isroot(y))
    {
      if(c[z][0]==y^c[y][0]==x) rotate(x);
      else rotate(y);
    }
    rotate(x);
  }
  pushup(x);
}
void access(int x)
{
  int t=0;
  while(x)
  {
    splay(x);
    c[x][1]=t;
    t=x;x=fa[x];
  }
}
void makeroot(int x)
{
  access(x);splay(x);rev[x]^=1;
}
void link(int x,int y)
{
  makeroot(x);access(y);splay(y);fa[x]=y;pushup(y);
}
int querymax(int x,int y)
{
  makeroot(x);access(y);splay(y);return mn[y];
}
int querymax2(int x,int y)
{
  makeroot(x);access(y);splay(y);return mn2[y];
}
int main()
{
  //freopen("1977.in","r",stdin);
  //freopen("1977.out","w",stdout);
  scanf("%d%d",&n,&m);
  for(int i=1;i<=m;i++) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].c);
  sort(e+1,e+m+1,cmp);
  for(int i=1;i<=n;i++) pa[i]=i;
  int hm=0;
  tot=n;
  for(int i=1;i<=m;i++)
  {
    int fu=find(e[i].u),fv=find(e[i].v);
    if(fu==fv) continue;
    ans+=e[i].c;
    //cout<<e[i].u<<' '<<e[i].v<<' '<<e[i].c<<endl;
    hm++;pa[fu]=fv;
    v[++tot]=e[i].c;link(e[i].u,tot);link(e[i].v,tot);
    flag[i]=1;
    if(hm==n-1) break;
  }
  for(int i=1;i<=m;i++)
  {
    if(flag[i]) continue;
    int v=querymax(e[i].u,e[i].v);
    //cout<<v<<' '<<e[i].c<<endl;
    if(v==e[i].c) v=querymax2(e[i].u,e[i].v);
    if(v>0) out=min(out,ans-v+e[i].c);
  }
  printf("%lld\n",out);
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值