[BZOJ2049]洞穴勘测[BZOJ2157]旅游Link-Cut Tree模板题

第一次写LCT,先来两道模板题,因为上个月被三道区间维护的超级工业的splay搞傻了,吸取了很多教训(见前几篇博客),所以LCT写得很顺啊,这里要感谢hzwer的题解。
BZOJ 2049:给出一颗树,每次加一条边或者删除一条边,询问两点的连通性。
这是一眼题,最裸的,纯天然的LCT,如果你不会先去看论文吧。半年以前看LCT感觉是一个非常高端的东西,当时反正看不懂,而TTY又有LCT爷的称号,写LCT都不用调,自然是伏地%。今天写了写发现还好。
废话不多说,查连通性就判断两个点的树的根是不是相等就可以了。

#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<cassert>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<climits>
#define X first
#define Y second
#define DB double
#define MP make_pair
#define LL long long
#define pb push_back
#define sqr(_) ((_)*(_))
#define INF 0x3f3f3f3f
#define pii pair<int,int>
#define pdd pair<DB,DB>
#define ull unsigned LL
#define DEBUG(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
void Read(int& x)
{
    x=0;int flag=0;char c;
    while(c=getchar())
    {
        if(c>='0'&&c<='9')x*=10,x+=c-'0',flag=1;
        else if(flag)break;
    }
}
const int MAXN=10010;
int n,m;
char s[10];
struct Link_Cut_Tree{
    int fa[MAXN],son[MAXN][2],rev[MAXN],top,st[MAXN];
    Link_Cut_Tree(){
        memset(fa,0,sizeof(fa));
        memset(son,0,sizeof(son));
        memset(rev,0,sizeof(rev));
        top=0;
    }
    bool isroot(int x)
    {
        return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;
    }
    void rotate(int x)
    {
        int y=fa[x],d=(x==son[y][1]);
        if(fa[y]&&!isroot(y))
        {
            son[fa[y]][son[fa[y]][1]==y]=x;
        }
        assert(x!=fa[y]);
        fa[x]=fa[y];
        assert(y!=x);
        fa[y]=x;
        son[y][d]=son[x][d^1];
        if(son[x][d^1])fa[son[x][d^1]]=y;
        son[x][d^1]=y;
    }
    void update_rev(int now)
    {
        rev[now]^=1;
        swap(son[now][0],son[now][1]);
    }
    void push_down(int x)
    {
        if(rev[x])
        {
            rev[x]^=1;
            update_rev(son[x][0]);
            update_rev(son[x][1]);
        }
    }
    void splay(int x)
    {
        st[++top]=x;
        int tmp=x;
        while(!isroot(tmp))
        {
            st[++top]=fa[tmp];
            tmp=fa[tmp];
        }
        while(top)
        {
            push_down(st[top]);
            top--;
        }
        while(!isroot(x))
        {
            int y=fa[x];
            if(isroot(y))
            {
                rotate(x);
                break;
            }
            rotate(x);
        }
    }
    void aksess(int x)
    {
        int tmp=0;
        while(x)
        {
            splay(x);
            son[x][1]=tmp;
            tmp=x;
            x=fa[x];
        }
    }
    void beroot(int x)
    {
        aksess(x);
        splay(x);
        update_rev(x);
    }
    void link(int x,int y)
    {
        beroot(x);
        fa[x]=y;
        splay(x);
    }
    void cut(int x,int y)
    {
        beroot(x);
        aksess(y);
        splay(y);
        son[y][0]=fa[x]=0;
    }
    int find(int x)
    {
        aksess(x);
        splay(x);
        int tmp=x;
        while(tmp)
        {
            if(!son[tmp][0])
                return tmp;
            tmp=son[tmp][0];
        }
    }
}LCT;
int main()
{
#ifndef ONLINE_JUDGE
    freopen("cave.in","r",stdin);
    freopen("cave.out","w",stdout);
#endif
    Read(n);Read(m);
    for(int i=1;i<=m;i++)
    {
        scanf("%s",s);
        if(s[0]=='Q')
        {
            int a,b;
            Read(a);Read(b);
            if(LCT.find(a)==LCT.find(b))
                puts("Yes");
            else
                puts("No");
        }
        else if(s[0]=='D')
        {
            int a,b;
            Read(a);Read(b);
            LCT.cut(a,b);
        }
        else
        {
            int a,b;
            Read(a);Read(b);
            LCT.link(a,b);
        }
    }
}

BZOJ 2157:给出一棵树,每次可以更改边权,路径边权置负,查询路径权值和,路径权值最大值,路径权值最小值。
虽然这一题既没有加边也没有删边,但是为了多A一道题而且能练习一下打改值标记的splay,就没有写树链剖分了。
LCT是不能维护边的权值的,我们考虑把边建成点,第i号边就是第n+i号点,然后也是模板题了。这里树上路径问题与区间维护问题有一点点不同,区间维护的splay要设两个虚点,取出区间就是把一个点splay到根,另一个点splay到根的右儿子,取出右儿子的子树。而LCT直接换根,aksess,再splay就可以了。

#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<cassert>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<climits>
#define X first
#define Y second
#define DB double
#define MP make_pair
#define LL long long
#define pb push_back
#define lc son[now][0]
#define rc son[now][1]
#define sqr(_) ((_)*(_))
#define INF 0x3f3f3f3f
#define pii pair<int,int>
#define pdd pair<DB,DB>
#define ull unsigned LL
#define DEBUG(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
void Read(int& x)
{
    x=0;int flag=0,sgn=1;char c;
    while(c=getchar())
    {
        if(c=='-')sgn=-1;
        else if(c>='0'&&c<='9')x*=10,x+=c-'0',flag=1;
        else if(flag)break;
    }
    x*=sgn;
}
const int MAXN=200011;
char s[10];
int n,m,f[MAXN];
struct Link_Cut_Tree{
    int fa[MAXN],son[MAXN][2],st[MAXN],top,mx[MAXN],sum[MAXN],mn[MAXN],v[MAXN];
    bool rev[MAXN],neg[MAXN];
    Link_Cut_Tree(){
        top=0;
        memset(rev,0,sizeof(rev)); memset(neg,0,sizeof(neg));
        memset(fa,0,sizeof(fa));   memset(son,0,sizeof(son));
        memset(mx,-INF,sizeof(mx));memset(mn,INF,sizeof(mn));
        memset(sum,0,sizeof(sum)); memset(v,0,sizeof(v));
    }
    void update_rev(int now)
    {
        rev[now]^=1;
        swap(lc,rc);
    }
    void update_neg(int now)
    {
        neg[now]^=1;
        sum[now]*=-1;
        swap(mx[now],mn[now]);
        mx[now]*=-1;
        mn[now]*=-1;
        v[now]*=-1;
    }
    void update(int now)
    {
        sum[now]=sum[lc]+sum[rc]+v[now];
        mn[now]=min(mn[lc],mn[rc]);
        mx[now]=max(mx[rc],mx[lc]);
        if(now>n)
        {
            mn[now]=min(mn[now],v[now]);
            mx[now]=max(mx[now],v[now]);
        }
    }
    void push_down(int now)
    {
        if(rev[now])
        {
            rev[now]^=1;
            update_rev(lc);
            update_rev(rc);
        }
        if(neg[now])
        {
            neg[now]^=1;
            update_neg(lc);
            update_neg(rc);
        }
    }
    bool isroot(int x)
    {
        return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;
    }   
    void rotate(int x)
    {
        int y=fa[x],d=(son[y][1]==x);
        if(fa[y]&&!isroot(y))
            son[fa[y]][son[fa[y]][1]==y]=x;
        fa[x]=fa[y];
        fa[y]=x;
        son[y][d]=son[x][d^1];
        if(son[x][d^1])
            fa[son[x][d^1]]=y;
        son[x][d^1]=y;
        update(y);update(x);
    }
    void splay(int x)
    {
        top=0;
        st[++top]=x;
        int tmp=x;
        while(!isroot(tmp))
        {
            st[++top]=fa[tmp];
            tmp=fa[tmp];
        }
        while(top)
            push_down(st[top--]);
        while(!isroot(x))
            rotate(x);
    }
    void aksess(int x)
    {
        int t=0;
        while(x)
        {
            splay(x);
            son[x][1]=t;
            update(x);
            t=x;x=fa[x];
        }
    }
    void beroot(int x)
    {
        aksess(x);
        splay(x);
        update_rev(x);
    }
    void link(int x,int y)
    {
        beroot(x);
        fa[x]=y;
        splay(x);
    }
    void split(int x,int y)
    {
        beroot(x);
        aksess(y);
        splay(y);
    }
}LCT;
int main()
{
#ifndef ONLINE_JUDGE
    freopen("tour.in","r",stdin);
    freopen("tour.out","w",stdout);
#endif
    scanf("%d",&n);
    int id=n;
    for(int i=1;i<n;i++)
    {
        //DEBUG("%d\n",i);
        int a,b,c;
        scanf("%d %d %d",&a,&b,&c);
        a++,b++;
        LCT.v[++id]=c,LCT.update(id);//插节点注意update
        f[i]=id;
        LCT.link(a,id),LCT.link(b,id);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%s",s);
        int x,y;
        Read(x),Read(y);
        if(s[0]=='C')
        {
            LCT.splay(f[x]);
            LCT.v[f[x]]=y;
            LCT.update(f[x]);
        }
        else if(s[0]=='N')
        {
            LCT.split(x+1,y+1);
            LCT.update_neg(y+1);
        }
        else if(s[0]=='S')
        {
            LCT.split(x+1,y+1);
            printf("%d\n",LCT.sum[y+1]);
        }
        else if(s[1]=='A')
        {
            LCT.split(x+1,y+1);
            printf("%d\n",LCT.mx[y+1]);
        }
        else
        {
            LCT.split(x+1,y+1);
            printf("%d\n",LCT.mn[y+1]);
        }
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值