LCT例题·BZOJ2049洞穴勘测、BZOJ3669魔法森林

4 篇文章 0 订阅

LCT例题·BZOJ2049洞穴勘测、BZOJ3669魔法森林

LCT是个好东西。

BZOJ2049洞穴勘测

原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=2049

题意:
n个点m个操作,操作有三种
1.Connect 连接两个点;
2.Destroy 删除两个点之间的连接;
3.Query 查询两个点是否连通;

题解:
LCT入门,只有几个基础操作
%黄学长
代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=10005;
struct node
{
    int fa,ch[2];
    bool rez;   
};
node tr[N];
int n,m;
struct Link_Cut_Tree
{
    void init(int x)
    {
        tr[x].fa=tr[x].ch[0]=tr[x].ch[0]=0; tr[x].rez=0;
    }
    bool isroot(int x)       //是否是该条链的splay的root 
    {
        return (tr[tr[x].fa].ch[0]!=x)&&(tr[tr[x].fa].ch[01]!=x);
    }
    void pushdown(int x)
    {
        if(tr[x].rez)
        {
            swap(tr[x].ch[0],tr[x].ch[1]);
            tr[x].rez^=1; tr[tr[x].ch[0]].rez^=1; tr[tr[x].ch[1]].rez^=1;
            return;
        }
    }
    void push(int x)
    {
        if(!isroot(x)) push(tr[x].fa);
        pushdown(x);
    }
    void rotate(int x)
    {
        int y=tr[x].fa; int z=tr[y].fa;
        int l,r;
        if(tr[y].ch[0]==x) l=0; else l=1; r=l^1;
        if(!isroot(y))
        {
            if(tr[z].ch[0]==y) tr[z].ch[0]=x; else tr[z].ch[1]=x;
        }
        tr[y].ch[l]=tr[x].ch[r]; tr[x].ch[r]=y; 
        tr[x].fa=z; tr[y].fa=x; tr[tr[y].ch[l]].fa=y;
    //  update(y); update(x);
    }
    void splay(int x)
    {
        push(x);
        while(!isroot(x))
        {
            int y=tr[x].fa; int z=tr[y].fa;
            if(!isroot(y))
            {
                if((tr[z].ch[0]==y)^(tr[y].ch[1]==x))   rotate(y);zag
                else rotate(x);/zig
            }
            rotate(x);
        }
    }
    void access(int x)
    {
        int y=0;
        for(;x;y=x,x=tr[x].fa)
        {
            splay(x);
            tr[x].ch[1]=y;//y的深度>x,y成为x的右儿子 
            //update(x);
        }
    }
    int findroot(int x)
    {
        access(x); splay(x);
        while(tr[x].ch[0]) x=tr[x].ch[0];
        return x;
    }
    void rever(int x)
    {
        access(x); splay(x); tr[x].rez^=1;
    }
    void link(int x,int y)
    {
        if(findroot(x)==findroot(y)) {puts("-1");return;}
        rever(x); tr[x].fa=y;
    }
    void cut(int x,int y)
    {
        if(findroot(x)!=findroot(y)) {puts("-1");return;}
        rever(x); access(y); splay(y);       //先把X搞成根了 所以深度较小 
        tr[y].ch[0]=tr[x].fa=0; //update(y);
    }
    void query(int x,int y)
    {
        if (findroot(x)==findroot(y)) printf("Yes\n");
        else printf("No\n");
    }
}LCT;

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<=n;i++)
    {
        LCT.init(i);
    }
    while(m--)
    {
        char opt[10]; int u,v;
        scanf("%s",opt); scanf("%d%d",&u,&v);
        if(opt[0]=='C')
        {
            LCT.link(u,v);
        }
        else if(opt[0]=='D')
        {
            LCT.cut(u,v);
        }
        else
        LCT.query(u,v); 
    }
    return 0;
}

BZOJ3669魔法森林

原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=2049

题意:给定一个图,每条边有ai、bi两种路径,选择一条1~n的路径,使得这条路径上max(ai)+max(bi)最小。

题解:这道题非常妙的一点在于把边看做一个点,连接着这条边的左右端点。
其次,先把ai排序,按照类似与做最小生成树的方法依次添加,若左右端点未联通,则直接link(端点u,代表边的节点)、link(端点v,代表边的节点)。若联通,则判断当前u-v路径上的最大bi值是否大于当前bi,若是,断原边,连现边。当达到1~n连通后,每次比较当前ans与记录的最小ans即可。
之前为什么更新时是直接比较b值有些不理解,其实是这样:

考虑直接用比较b值的正确性:
1、 如果加入这条边前1,N不连通,则后面一定会加入边,由于a值递增,后面的a值比现在两条边的a 值都要大,因此当前边的a值大小不会影响。
2、如果加入这条边前1,N已联通,则之前的最小答案已经记录,不会对最终答案进行影响。

ps.这里学长对ai<=30,bi<=50000的子问题给了个思路,固定一个量,枚举ai,对满足条件的边的bi构造最小生成树。

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=50005;
const int M=100005;
int val[M+N];
const int inf=0x7fffffff;
int ans;
struct eadge
{
    int u,v,a,b;
}e[M];
bool cmp(const eadge &A,const eadge &B)
{
    return A.a<B.a;
}
struct node
{
    int ch[2],fa;
    int pos,maxn;
    bool rev;
};
node tr[M+N];
struct Link_Cut_Tree
{
    void init(int x)
    {
        tr[x].fa=tr[x].ch[0]=tr[x].ch[1]=tr[x].pos=tr[x].maxn=tr[x].rev=0;
    }
    void pushdown(int x)
    {
        if(!x) return;
        if(tr[x].rev)
        {
            swap(tr[x].ch[0],tr[x].ch[1]);
            tr[tr[x].ch[0]].rev^=1;
            tr[tr[x].ch[1]].rev^=1;
            tr[x].rev^=1;
        }
    }
    void push(int x)
    {
        if(!isroot(x)) push(tr[x].fa);
        pushdown(x);        
    }
    void update(int x)
    {
        if(!x) return;
        tr[x].pos=x;    tr[x].maxn=val[x]; //因为其左右子树都有可能更改,因此不能根据ch[0/1]更改,而是从自己val开始 
        if(tr[x].ch[0])  
            if(val[tr[x].pos]<val[tr[tr[x].ch[0]].pos]) {tr[x].maxn=tr[tr[x].ch[0]].maxn; tr[x].pos=tr[tr[x].ch[0]].pos;}              
        if(tr[x].ch[1])
            if(val[tr[x].pos]<val[tr[tr[x].ch[1]].pos]) {tr[x].maxn=tr[tr[x].ch[1]].maxn; tr[x].pos=tr[tr[x].ch[1]].pos;}   
    }
    bool isroot(int x)
    {
        return (tr[tr[x].fa].ch[0]!=x)&&(tr[tr[x].fa].ch[1]!=x);
    }
    void rotate(int x)
    {
        int y=tr[x].fa; int z=tr[y].fa;
        int l,r;
        if(tr[y].ch[0]==x) l=0; else l=1; r=l^1;
        if(!isroot(y))
        {
            if(tr[z].ch[0]==y) tr[z].ch[0]=x;
            else tr[z].ch[1]=x;
        }
        tr[y].ch[l]=tr[x].ch[r]; tr[tr[x].ch[r]].fa=y;
        tr[x].ch[r]=y;  tr[y].fa=x;   tr[x].fa=z;
        update(y); update(x);   
    }
    void splay(int x)
    {
        push(x);
        while(!isroot(x))
        {
            int y=tr[x].fa; int z=tr[y].fa;
            if(!isroot(y))
            {
                if((tr[z].ch[0]==y)^(tr[y].ch[0]==x)) rotate(y);
                else rotate(x); 
            }
            rotate(x);  
        }
    }
    void access(int x)
    {
        int y=0;
        for(;x;y=x,x=tr[x].fa)
        {
            splay(x);
            tr[x].ch[1]=y;
            update(x);  
        }           
    }   
    int findroot(int x)
    {
        access(x); splay(x);
        while(tr[x].ch[0]!=0) x=tr[x].ch[0];
        return x;
    }
    void rever(int x)
    {
        access(x);splay(x); tr[x].rev^=1;       
    }
    void link(int x,int y)
    {
        rever(x); tr[x].fa=y;
    }
    void cut(int x,int y)
    {
        if(findroot(x)!=findroot(y)) return;
        rever(x); access(y); splay(y);
        tr[x].fa=tr[y].ch[0]=0;
        update(y);
    }
    int query(int x,int y)
    {
        rever(x); access(y); splay(y);
        return tr[y].pos;
    }
}LCT;
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    memset(val,0,sizeof(val));
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].a,&e[i].b);
    }
    sort(e+1,e+m+1,cmp);
    for(int i=1;i<=m;i++)
    {
        val[n+i]=e[i].b;
    }
    for(int i=0;i<=n+m;i++)
    {
        LCT.init(i);
    }
    ans=inf;
    for(int i=1;i<=m;i++)
    {
        int fx=LCT.findroot(e[i].u); int fy=LCT.findroot(e[i].v);
        if(fx!=fy) 
        {

            LCT.link(e[i].u,i+n);
            LCT.link(i+n,e[i].v);
        }
        else
        {
            int k=LCT.query(e[i].u,e[i].v);         
            if(val[k]>e[i].b)                       
            {                                       
                LCT.cut(e[i].u,k);                  
                LCT.cut(e[i].v,k);
                LCT.link(e[i].u,i+n);
                LCT.link(e[i].v,i+n);       
            }   
        }
        if(LCT.findroot(1)==LCT.findroot(n))    ans=min(ans,val[LCT.query(1,n)]+e[i].a);
    }
    if(ans!=inf)
    printf("%d",ans);
    else printf("-1");
    return 0;
}

最后安利dalao的浅谈LCT:
http://blog.csdn.net/NOIAu/article/details/75451616
写得真心好。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值