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

来源:互联网 发布:淘宝网的域名怎么设置 编辑:程序博客网 时间:2024/05/21 11:33

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
写得真心好。

原创粉丝点击