BZOJ 2959 长跑 Link-Cut-Tree+并查集

22 篇文章 0 订阅
17 篇文章 1 订阅

题目大意:给定n个点,支持以下操作:
1.在某两个点之间连接一条无向边
2.改变某个点的权值
3.将每条边设定一个方向,然后从 x 走到y,求能经过的所有点的权值和
首先如果这个图是静态的,我们把边双都缩点,那么每次询问显然就是两个点所在边双路径上的点权和
现在图是动态的,因此我们用动态树维护一下就行了
如果连边的两个点不连通,就在LCT中连接这两个点
如果连边的两个点已经连通,就将这个两个点路径上的所有点用并查集缩点
时间复杂度 O(mlogn)
似乎用链剖会快一些?

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 150100
using namespace std;
int n,m;
int a[M];
struct Union_Find_Set{
    int fa[M];
    int Find(int x)
    {
        if(!fa[x]||fa[x]==x)
            return fa[x]=x;
        return fa[x]=Find(fa[x]);
    }
    void Union(int x,int y)
    {
        x=Find(x);y=Find(y);
        if(x==y) return ;
        fa[x]=y;
    }
}s1,s2;
namespace Link_Cut_Tree{
    struct abcd{
        abcd *ls,*rs,*fa;
        int val,sum;
        bool rev_mark;
        abcd() {}
        abcd(int x);
        void Push_Up();
        void Push_Down();
        void Reverse();
    }tree[M],*null=&tree[0];
    abcd :: abcd(int x)
    {
        ls=rs=fa=null;
        val=sum=x;
        rev_mark=false;
    }
    void abcd :: Push_Up()
    {
        sum=ls->sum+rs->sum+val;
    }
    void abcd :: Push_Down()
    {
        if(fa->ls==this||fa->rs==this)
            fa->Push_Down();
        if(rev_mark)
        {
            ls->Reverse();
            rs->Reverse();
            rev_mark=false;
        }
    }
    void abcd :: Reverse()
    {
        swap(ls,rs);
        rev_mark^=1;
    }
    void Zig(abcd *x)
    {
        abcd *y=x->fa;
        y->ls=x->rs;
        x->rs->fa=y;
        x->rs=y;
        x->fa=y->fa;
        if(y==y->fa->ls)
            y->fa->ls=x;
        else if(y==y->fa->rs)
            y->fa->rs=x;
        y->fa=x;
        y->Push_Up();
    }
    void Zag(abcd *x)
    {
        abcd *y=x->fa;
        y->rs=x->ls;
        x->ls->fa=y;
        x->ls=y;
        x->fa=y->fa;
        if(y==y->fa->ls)
            y->fa->ls=x;
        else if(y==y->fa->rs)
            y->fa->rs=x;
        y->fa=x;
        y->Push_Up();
    }
    void Splay(abcd *x)
    {
        x->Push_Down();
        while(x->fa->ls==x||x->fa->rs==x)
        {
            abcd *y=x->fa,*z=y->fa;
            if(x==y->ls)
            {
                if(y==z->ls)
                    Zig(y);
                Zig(x);
            }
            else
            {
                if(y==z->rs)
                    Zag(y);
                Zag(x);
            }
        }
        x->Push_Up();
    }
    void Access(abcd *x)
    {
        abcd *y=null;
        while(x!=null)
        {
            Splay(x);
            x->rs=y;
            x->Push_Up();
            x->fa=&tree[s2.Find(x->fa-tree)];
            y=x;x=x->fa;
        }
    }
    void Move_To_Root(abcd *x)
    {
        Access(x);
        Splay(x);
        x->Reverse();
    }
    void Link(abcd *x,abcd *y)
    {
        Move_To_Root(x);
        x->fa=y;
    }
    void Reduction(abcd *&x,abcd *y)
    {
        if(x==null)
            return ;
        s2.Union(x-tree,y-tree);
        y->val+=x->val;
        Reduction(x->ls,y);
        Reduction(x->rs,y);
        x=null;
    }
}
namespace IStream{
    #define L (1<<16)
    char Get_Char()
    {
        static char buffer[M],*S,*T;
        if(S==T)
        {
            T=(S=buffer)+fread(buffer,1,L,stdin);
            if(S==T) return EOF;
        }
        return *S++;
    }
    int Get_Int()
    {
        int re=0;
        char c=Get_Char();
        while(c<'0'||c>'9')
            c=Get_Char();
        while(c>='0'&&c<='9')
            re=(re<<3)+(re<<1)+(c-'0'),c=Get_Char();
        return re;
    }
}
int main()
{
    using namespace Link_Cut_Tree;
    using namespace IStream;
    int i,p,x,y;
    cin>>n>>m;
    for(i=1;i<=n;i++)
    {
        a[i]=Get_Int();
        tree[i]=abcd(a[i]);
    }
    for(i=1;i<=m;i++)
    {
        p=Get_Int();
        x=Get_Int();
        y=Get_Int();
        if(p==1)
        {
            x=s2.Find(x);y=s2.Find(y);
            if(x==y) continue ;
            if(s1.Find(x)!=s1.Find(y))
                Link(&tree[x],&tree[y]),s1.Union(x,y);
            else
            {
                Move_To_Root(&tree[x]);
                Access(&tree[y]);
                Splay(&tree[y]);
                Reduction(tree[y].ls,&tree[y]);
                Reduction(tree[y].rs,&tree[y]);
                tree[y].Push_Up();
            }
        }
        else if(p==2)
        {
            Splay(&tree[s2.Find(x)]);
            tree[s2.Find(x)].val+=y-a[x];
            tree[s2.Find(x)].Push_Up();
            a[x]=y;
        }
        else
        {
            x=s2.Find(x);y=s2.Find(y);
            if(s1.Find(x)!=s1.Find(y))
                puts("-1");
            else
            {
                Move_To_Root(&tree[x]);
                Access(&tree[y]);
                Splay(&tree[y]);
                printf("%d\n",tree[y].sum);
            }
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值