2959: 长跑 LCT+dfs

不错的一道题目,可惜tsinsen被卡常了,在BZOJ A掉了。
由于我们可以多次经过一个点,那么如果形成了一个简单环,那么环内的每个点都可以被经过,所以可以把这样的环缩成一个点,权值就是原来所有点的权值之和。
这样每次连边的时候如果发现有环就缩点,用并查集维护。
然后就是裸的LCT。

#include<iostream>
#include<cstdio>
using namespace std;

const int N=150005;
int fa[N],f[N],g[N],q[N],tree[N][2],sum[N],val[N],a[N],rev[N];
int n,m;

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

int find1(int x)
{
    return f[x]==x?x:f[x]=find1(f[x]);
}

int find2(int x)
{
    return g[x]==x?x:g[x]=find2(g[x]);
}

inline bool isroot(int x)
{
    return tree[find2(fa[x])][0]!=x&&tree[find2(fa[x])][1]!=x;
}

inline void pushup(int x)
{
    sum[x]=sum[tree[x][0]]+sum[tree[x][1]]+val[x];
}

inline void pushdown(int x)
{
    if (rev[x])
    {
        rev[x]^=1; rev[tree[x][0]]^=1; rev[tree[x][1]]^=1;
        swap(tree[x][0],tree[x][1]);
    }
}

inline void rotate(int x)
{
    int y=find2(fa[x]),z=find2(fa[y]),l=tree[y][1]==x,r=l^1;
    if (!isroot(y)) tree[z][tree[z][1]==y]=x;
    fa[x]=z; fa[y]=x; fa[tree[x][r]]=y;
    tree[y][l]=tree[x][r]; tree[x][r]=y;
    pushup(y);
}

inline void splay(int x)
{
    int top=0; q[++top]=x;
    for (int i=x;!isroot(i);i=find2(fa[i])) q[++top]=find2(fa[i]);
    while (top) pushdown(q[top--]);
    while (!isroot(x))
    {
        int y=find2(fa[x]),z=find2(fa[y]);
        if (!isroot(y))
        {
            if (tree[y][0]==x^tree[z][0]==y) rotate(x); else rotate(y);
        }
        rotate(x);
    }
    pushup(x);
}

inline void access(int x)
{
    for (int t=0;x;t=x,x=find2(fa[x]))
        splay(x),tree[x][1]=t,pushup(x);
}

inline void makeroot(int x)
{
    access(x); splay(x); rev[x]^=1;
}

inline void link(int x,int y)
{
    makeroot(x); fa[x]=y;
}

void dfs(int x,int F)
{
    if (!x) return;
    pushdown(x);
    g[x]=F;
    if (x!=F) val[F]+=val[x];
    dfs(tree[x][0],F); dfs(tree[x][1],F);
    tree[x][0]=tree[x][1]=0;
}

int main()
{
    n=read(); m=read();
    for (int i=1;i<=n;i++) a[i]=val[i]=read();
    for (int i=1;i<=n;i++) f[i]=g[i]=i;
    for (int i=1;i<=m;i++)
    {
        int opt=read(),x=read(),y=read();
        if (opt==1)
        {
            int p=find2(x),q=find2(y);
            if (p==q) continue;
            int P=find1(x),Q=find1(y);
            if (P!=Q) f[P]=Q,link(p,q);
            else makeroot(p),access(q),splay(q),dfs(q,q),pushup(q);
        }
        else if (opt==2)
        {
            int now=find2(x);
            splay(now);
            val[now]+=y-a[x];
            a[x]=y;
            pushup(now);
        }
        else
        {
            int p=find2(x),q=find2(y);
            int P=find1(x),Q=find1(y);      
            if (P!=Q) puts("-1");
            else makeroot(p),access(q),splay(q),printf("%d\n",sum[q]);
        }
    }
    return 0;
}       
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值