BZOJ2959: 长跑

BZOJ2959: 长跑

LCT·并查集

题解:

如果没有环就是LCT裸题了,维护子树和即可。

有环的时候,不管环长得什么样(单环也好,多个环有交点、有公用边也好),只要依次为环上的边定向,一定可以做到从任意一个点开始,遍历环上所有的点,在任意一个点停下来(当然了,可能会走重复的路径)。也就是说,环跟一个点没有区别。

那么就可以缩点,用并查集维护,剩下的部分LCT。

LCT合并的时候直接选出一个代表点,把其他点打散,权值加到代表点里即可。不需要关心连向其他点的虚边,因为有特殊的access,详见代码。

注意1:在调用LCT方法需要传当前节点的指针时,一定先用并查集查一下实际应该用哪个点。特别是access,详见代码。

注意2:修改点权时不要直接x->w=w,因为x可能代表环中的多个点,一定要使用增量。详见代码。

Code:

#include <iostream>
#include <cstring>
#include <cstdio>
#define D(x) cout<<#x<<" = "<<x<<"  "
#define E cout<<endl
using namespace std;
const int N = 200005;

int n,m, cnt[N];

struct UnionFindSet{
    int pa[N]; int sz;
    void init(int _sz){ sz=_sz; for(int i=1;i<=sz;i++)pa[i]=i; }
    int find(int x){ if(pa[x]!=x)pa[x]=find(pa[x]); return pa[x]; }
} f1, f2;

struct Node{
    Node *pa, *ch[2];
    int sum, w, tag;
    Node(){ pa=ch[1]=ch[2]=NULL; sum=w=tag=0; }
    void rev(){ tag^=1; swap(ch[0],ch[1]); }
    void update(){ sum=w; if(ch[0])sum+=ch[0]->sum; if(ch[1])sum+=ch[1]->sum; }
    void pushdown(){ if(tag){ tag=0; if(ch[0])ch[0]->rev(); if(ch[1])ch[1]->rev(); } }
    int getwh(){ if(!pa)return -1; if(pa->ch[0]==this)return 0; if(pa->ch[1]==this)return 1; return -1; }
    void setch(Node *child,int wh){ ch[wh]=child; if(child)child->pa=this; update(); }
    bool isroot(){ return getwh()==-1; }
} pool[N], *s[N];

void dfs(Node *x){
    printf("dfs: %d\n",x-pool);
    if(x->ch[0])dfs(x->ch[0]); if(x->ch[1])dfs(x->ch[1]);
}

void rotate(Node *x){
    Node *y=x->pa, *z=x->pa->pa; int wh=x->getwh();
    if(y->isroot()) x->pa=z; else z->setch(x,y->getwh());
    y->setch(x->ch[wh^1],wh); x->setch(y,wh^1);
}
void splay(Node *x){
    Node *y; int top=0;
    for(y=x;!y->isroot();y=y->pa)s[++top]=y; s[++top]=y;
    for(;top;top--)s[top]->pushdown();
    for(;!x->isroot();rotate(x)){
        if(!x->pa->isroot()) 
            x->getwh()==x->pa->getwh() ? rotate(x->pa) : rotate(x);
    }
} 

void access(Node *x){
    Node *t=NULL; 
    while(x){ splay(x); x->setch(t,1); t=x; if(!x->pa)break; x=&pool[f2.find(x->pa-pool)]; }
}
void makeroot(Node *x){ access(x); splay(x); x->rev(); }
void link(Node *x,Node *y){ makeroot(x); x->pa=y; }

void merge(Node *x,Node *y){
//  if(x!=y)printf("merge: %d %d\n",x-pool,y-pool);
    int a=x-pool, b=y-pool; f2.pa[a]=b;
    x->pushdown(); if(x!=y)y->w+=x->w;
    if(x->ch[0])merge(x->ch[0],y); if(x->ch[1])merge(x->ch[1],y);
    x->ch[0]=x->ch[1]=NULL;
}
void ins(Node *x,int w){ splay(x); x->w+=w; x->update(); }
int qsum(Node *x,Node *y){ 
//  printf("qsum: %d %d\n",x-pool,y-pool); 
    makeroot(x); access(y); splay(y); 
//  dfs(y); 
    return y->sum; 
}

int main(){
    freopen("a.in","r",stdin);
    scanf("%d%d",&n,&m); 
    int op,a,b; f1.init(n); f2.init(n);
    for(int i=1;i<=n;i++){ scanf("%d",&cnt[i]); ins(&pool[i],cnt[i]); }
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&op,&a,&b);
        if(op==1){
            int u=f1.find(a), v=f1.find(b);
            int x=f2.find(a), y=f2.find(b);
            Node *xx=&pool[x], *yy=&pool[y];
            if(x!=y){
                if(u!=v){ link(xx,yy); f1.pa[u]=v; }
                else{ makeroot(xx); access(yy); splay(yy); merge(yy,yy); }
            }
        }
        else if(op==2){ ins(&pool[f2.find(a)],b-cnt[a]); cnt[a]=b; }
        else{ 
            if(f1.find(a)!=f1.find(b)) puts("-1");
            else printf("%d\n",qsum(&pool[f2.find(a)],&pool[f2.find(b)])); 
        }
    } 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值