BZOJ 3669 [Noi2014]魔法森林

题目链接

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

题解

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

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

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

  1. 如果x y y 不连通,那么加入这条边不会对答案产生不利的影响。
  2. 如果x y y 连通,这个时候分两种情况考虑。
    1. x y y Bi最大值 bi ≤ b i ,加入这条边必定不会使 1 1 n的边权最大值变小,因此选择不加入。

      • x x y Bi B i 最大值 >bi > b i ,此时删去 x x y中边权最大的一个,必定不会使 1 1 n的边权最大值变大,因此可以这样操作。
      • 可以证明,通过上述操作,得出的解一定是最优的。

        代码

        #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;
        }
        
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值