题目链接

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

题解

将一个a-b,权值为c的边看成a-e-b,其中a,b权值为0,e的权值为c,这样就把边权变成了点权。

首先将所有边按照Ai排序,用LCT维护点和边的连通情况,边权为Bi

考虑新加入一条从xy,权值为ai,bi的边。显然,如果这条边对1n的答案有影响,那么答案就可以对ai+max(1nBi)min。如果这条边对答案没有影响,那么答案必定已经前述式子,对前述式子更新不会对答案有任何影响(ai不是最小的,+号后面值的是相同的)。

  1. 如果xy不连通,那么加入这条边不会对答案产生不利的影响。
  2. 如果xy连通,这个时候分两种情况考虑。
    1. xyBi最大值bi,加入这条边必定不会使1n的边权最大值变小,因此选择不加入。
    2. xyBi最大值>bi,此时删去xy中边权最大的一个,必定不会使1n的边权最大值变大,因此可以这样操作。

可以证明,通过上述操作,得出的解一定是最优的。

代码

#include <cstdio>
#include <algorithm>

int read()
{
  int x=0,f=1;
  char ch=getchar();
  while((ch<'0')||(ch>'9'))
    {
      if(ch=='-')
        {
          f=-f;
        }
      ch=getchar();
    }
  while((ch>='0')&&(ch<='9'))
    {
      x=x*10+ch-'0';
      ch=getchar();
    }
  return x*f;
}

const int maxn=50000;
const int maxm=100000;
const int maxk=maxn+maxm;
const int inf=0x3f3f3f3f;

struct node
{
  node *son[2],*fa,*pos;
  int self,val,rev;
};

node tree[maxk+10];

namespace lct
{
  int isroot(node *x)
  {
    if(x->fa==NULL)
      {
        return 1;
      }
    return !((x->fa->son[0]==x)||(x->fa->son[1]==x));
  }

  int t(node *x)
  {
    return x->fa->son[1]==x;
  }

  int pushdown(node *x)
  {
    if(x->rev)
      {
        x->rev=0;
        std::swap(x->son[0],x->son[1]);
        if(x->son[0]!=NULL)
          {
            x->son[0]->rev^=1;
          }
        if(x->son[1]!=NULL)
          {
            x->son[1]->rev^=1;
          }
      }
    return 0;
  }

  int updata(node *x)
  {
    int l=(x->son[0]==NULL)?0:x->son[0]->val,r=(x->son[1]==NULL)?0:x->son[1]->val;
    x->val=std::max(x->self,std::max(l,r));
    x->pos=(x->self>=std::max(l,r))?x:((l>r)?x->son[0]->pos:x->son[1]->pos);
    return 0;
  }

  int rotate(node *x)
  {
    node *f=x->fa;
    pushdown(f);
    pushdown(x);
    int k=t(x);
    if(!isroot(f))
      {
        f->fa->son[t(f)]=x;
      }
    x->fa=f->fa;
    f->fa=x;
    if(x->son[k^1]!=NULL)
      {
        x->son[k^1]->fa=f;
      }
    f->son[k]=x->son[k^1];
    x->son[k^1]=f;
    updata(x);
    updata(f);
    return 0;
  }

  int splay_root(node *x)
  {
    while(!isroot(x))
      {
        node *f=x->fa;
        if(isroot(f))
          {
            rotate(x);
          }
        else if(t(f)==t(x))
          {
            rotate(f);
            rotate(x);
          }
        else
          {
            rotate(x);
            rotate(x);
          }
      }
    pushdown(x);
    return 0;
  }

  int access(node *x)
  {
    node *y=NULL;
    while(x!=NULL)
      {
        splay_root(x);
        x->son[1]=y;
        if(y!=NULL)
          {
            y->fa=x;
          }
        updata(x);
        y=x;
        x=y->fa;
      }
    return 0;
  }

  int makeroot(node *x)
  {
    access(x);
    splay_root(x);
    x->rev^=1;
    return 0;
  }

  int link(node *x,node *y)
  {
    makeroot(x);
    x->fa=y;
    return 0;
  }

  int cut(node *x,node *y)
  {
    makeroot(x);
    access(y);
    splay_root(y);
    y->son[0]=x->fa=NULL;
    updata(y);
    return 0;
  }

  node* find(node *x)
  {
    access(x);
    splay_root(x);
    while(x->son[0]!=NULL)
      {
        x=x->son[0];
        pushdown(x);
      }
    return x;
  }

  int check(node *x,node *y)
  {
    return find(x)==find(y);
  }

  node* getmax(node *x)
  {
    splay_root(x);
    int v=x->val;
    while(x!=NULL)
      {
        if(x->self==v)
          {
            return x;
          }
        if((x->son[0]!=NULL)&&(x->son[0]->val==v))
          {
            x=x->son[0];
          }
        else
          {
            x=x->son[1];
          }
        pushdown(x);
      }
    return 0;
  }

  node* getpn(node *x,int op)
  {
    splay_root(x);
    x=x->son[op];
    pushdown(x);
    while(x->son[op^1]!=NULL)
      {
        x=x->son[op^1];
        pushdown(x);
      }
    return x;
  }

  int check_link(node *x,node *y,node *e)
  {
    if(check(x,y))
      {
        makeroot(x);
        access(y);
        splay_root(y);
        if(y->val>e->self)
          {
            node *d=y->pos;
            node *p=getpn(d,0),*q=getpn(d,1);
            cut(d,p);
            cut(d,q);
          }
        else
          {
            return 0;
          }
      }
    link(e,x);
    link(e,y);
    return 0;
  }

  int getmax(node *x,node *y)
  {
    makeroot(x);
    access(y);
    splay_root(y);
    return y->val;
  }
}

struct edge
{
  int x,y,a,b;

  bool operator <(const edge &other) const
  {
    if(a==other.a)
      {
        return b<other.b;
      }
    return a<other.a;
  }
};

edge e[maxm+10];
int n,m;

int main()
{
  n=read();
  m=read();
  for(int i=1; i<=m; ++i)
    {
      e[i].x=read();
      e[i].y=read();
      e[i].a=read();
      e[i].b=read();
    }
  std::sort(e+1,e+m+1);
  for(int i=1; i<=n; ++i)
    {
      tree[i].son[0]=tree[i].son[1]=tree[i].fa=NULL;
      tree[i].self=tree[i].val=tree[i].rev=0;
    }
  int ans=inf;
  node *start=&tree[1],*end=&tree[n];
  for(int i=1; i<=m; ++i)
    {
      tree[i+n].son[0]=tree[i+n].son[1]=tree[i+n].fa=NULL;
      tree[i+n].self=tree[i+n].val=e[i].b;
      tree[i+n].rev=0;
      node *l=&tree[e[i].x],*r=&tree[e[i].y],*ed=&tree[i+n];
      lct::check_link(l,r,ed);
      if(lct::check(start,end))
        {
          ans=std::min(ans,e[i].a+lct::getmax(start,end));
        }
    }
  printf("%d\n",(ans==inf)?-1:ans);
  return 0;
}