BZOJ2959 长跑

BZOJ2959 长跑


题目描述:

传送门

题目分析:

首先发现这个题要进行动态连边操作。不管了先把lct板子打上去。然后仔细看一下,题目中那个只能朝边的一个方向跑的条件其实就限制图的状态在缩完点之后始终是一棵树。那么询问操作实际上就是询问了一条固定的链的权值和。考虑使用lct维护,同时在外面用一个并查集判断连通性。首先当连接两个点的时候先findroot一下看是否已经联通,如果联通的话就暴力循环整个子树然后暴力合并并查集,同时把已经缩完的点的信息清零。当修改的时候,我们只需要更改这个点值与之前的值得差就可以维护。

#include <bits/stdc++.h>
using namespace std;
#define lc ch[x][0]
#define rc ch[x][1]
const int MAXN=3e5+7;
int ch[MAXN][2],r[MAXN],fa[MAXN],f[MAXN],st[MAXN];
int s[MAXN],v[MAXN],add[MAXN],n,m,q[MAXN];
inline int find(int x){return fa[x]==x?fa[x]:fa[x]=find(fa[x]);}
inline bool nroot(int x){return ch[f[x]][0]==x||ch[f[x]][1]==x;}
inline void pushr(int x){swap(lc,rc);r[x]^=1;}
inline void pushup(int x){s[x]=add[x]+s[lc]+s[rc];}
inline void pushdown(int x)
{
    if(r[x]){
        if(lc) pushr(lc);
        if(rc) pushr(rc);
        r[x]=0;
    }
}
inline void rotate(int x)
{
    int y=f[x],z=f[y],kind=ch[y][1]==x,w=ch[x][!kind];
    if(nroot(y)) ch[z][ch[z][1]==y]=x;ch[x][!kind]=y;ch[y][kind]=w;
    if(w) f[w]=y;f[x]=z;f[y]=x;
    pushup(y);
}
inline void splay(int x)
{
    int y=x,z=0;
    st[++z]=y;
    while(nroot(y)) st[++z]=y=f[y];
    while(z) pushdown(st[z--]);
    while(nroot(x)){
        y=f[x],z=f[y];
        if(nroot(y)) rotate((ch[z][0]==y)^(ch[y][0]==x)?x:y);
        rotate(x);
    }
    pushup(x);
}
inline void access(int x)
{
    for(int y=0;x;y=x,x=f[y]=find(f[x]))
    splay(x),rc=y,pushup(x);
}
inline void makeroot(int x)
{
    access(x);splay(x);pushr(x);
}
inline int findroot(int x)
{
    access(x);splay(x);
    while(lc) pushdown(x),x=lc;
    splay(x);
    return x;
}
inline void split(int x,int y)
{
    makeroot(x);access(y);splay(y);
}
inline void merge(int x,int y)
{
    if(x==y) return;
    makeroot(x);
    if(findroot(y)!=x){
        f[x]=y;
        return;
    }
    makeroot(y);
    access(x);splay(x);
    int cnt=s[x];
    int L=0,R=0;
    q[++R]=x;
    while(L<R){
        x=q[++L];
        if(lc) q[++R]=lc;
        if(rc) q[++R]=rc;
        fa[x]=y;
        add[x]=s[x]=f[x]=lc=rc=0;
    }
    add[y]=s[y]=cnt;
    f[y]=0;
}
inline void change(int x,int y,int k)
{
    access(x);splay(x);
    s[x]+=k-v[y];add[x]+=k-v[y];
    pushup(x);
}
inline int read()
{
    int x=0,c=1;
    char ch=' ';
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    while(ch=='-')c*=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*c;
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++){
        v[i]=read();s[i]=add[i]=v[i];
        fa[i]=i;
    }
    for(int i=1;i<=m;i++){
        int opt,x,y;
        opt=read();x=read();y=read();
        if(opt==1){
            x=find(x);y=find(y);
            merge(x,y);
        } else if(opt==2){
            int tl=find(x);
            if(v[x]!=y) change(tl,x,y);
            v[x]=y;
        } else {
            x=find(x);y=find(y);
            if(findroot(x)!=findroot(y)) puts("-1");
            else{
                split(x,y);
                printf("%d\n", s[y]);
            }
        }
    }
}

转载于:https://www.cnblogs.com/victorique/p/10207071.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值