BZOJ2959 长跑

LCT 并查集

BZOJ题目传送门

LCT维护双连通分量。

如果没有1操作可以Tarjan+树剖水过,现在是动态的话就用LCT维护。

A A B不连通时直接连起来。当 A A B已经连通时,把它们所在的双连通分量缩到一个点上。具体操作为先把 A A B搞到同一个Splay里,然后遍历Splay,用并查集把所有点都缩到一个点上去并把权值赋给它。

判断是否连通可以直接用LCT,然而我太菜所以 T了就又开了一个并查集。

代码:

#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 150005
#define F inline
#define V F void
using namespace std;
typedef long long LL;
struct tree{ int fa,to[2],v; LL x; bool f; }t[N];
int n,m,tp,w[N],f1[N],f2[N],stk[N]; LL tmp;
F char readc(){
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    return l==r?EOF:*l++;
}
F int _read(){
    int x=0; char ch=readc();
    while (!isdigit(ch)) ch=readc();
    while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
    return x;
}
V writec(LL x){ if (x>9) writec(x/10); putchar(x%10+48); }
V _write(LL x){ writec(x),puts(""); }
int findfa(int *fa,int x){ return x==fa[x]?x:fa[x]=findfa(fa,fa[x]); }
#define f(x) findfa(f2,t[x].fa)
#define rt(x) (t[f(x)].to[0]!=(x)&&t[f(x)].to[1]!=(x))
V pshp(int x){ t[x].x=t[x].v+t[t[x].to[0]].x+t[t[x].to[1]].x; }
V pshd(int x){
    if (!t[x].f) return; int &l=t[x].to[0],&r=t[x].to[1];
    t[x].f=0,swap(l,r),t[l].f^=1,t[r].f^=1;
}
V rtt(int x){
    int y=f(x),z=f(y),l=t[y].to[0]==x;
    if (!rt(y)) t[z].to[t[z].to[0]!=y]=x;
    t[x].fa=z,t[y].fa=x,t[t[x].to[l]].fa=y;
    t[y].to[l^1]=t[x].to[l],t[x].to[l]=y;
    pshp(y),pshp(x);
}
V splay(int x){
    for (int i=stk[tp=1]=x;!rt(i);i=f(i))
        stk[++tp]=f(i);
    while (tp) pshd(stk[tp--]);
    for (int y=f(x),z=f(y);!rt(x);rtt(x),y=f(x),z=f(y))
        if (!rt(y)) rtt(t[z].to[0]==y^t[y].to[0]==x?x:y);
}
V ccss(int x){ for (int i=0;x;i=x,x=f(x)) splay(x),t[x].to[1]=i,pshp(x); }
V mkrt(int x){ ccss(x),splay(x),t[x].f^=1; }
V mrg(int x,int y){
    int &l=t[x].to[0],&r=t[x].to[1];
    if (l) t[y].v+=t[l].v,mrg(l,y);
    if (r) t[y].v+=t[r].v,mrg(r,y);
    f2[x]=y,l=r=0;
}
V sprt(int x,int y){ mkrt(x),ccss(y),splay(y); }
V lnk(int x,int y){
    int fx=findfa(f1,x),fy=findfa(f1,y);
    if (findfa(f1,x)!=findfa(f1,y))
        mkrt(x),t[x].fa=y,f1[f1[x]]=f1[y];
    else sprt(x,y),mrg(y,y),pshp(y);
}
F LL srch(int x,int y){
    if (findfa(f1,x)!=findfa(f1,y)) return -1;
    sprt(x,y); return t[y].x;
}
int main(){
    n=_read(),m=_read();
    for (int i=1;i<=n;i++)
        w[i]=t[i].v=_read(),f1[i]=f2[i]=i,pshp(i);
    while (m--){
        int f=_read(),x=_read(),y=_read(),fx=findfa(f2,x);
        if (f==1) lnk(fx,findfa(f2,y));
        else if (f==2) mkrt(fx),t[fx].v+=y-w[x],w[x]=y,pshp(fx);
        else (tmp=srch(fx,findfa(f2,y)))!=-1?_write(tmp):(void)puts("-1");
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值