[省选前题目整理][BZOJ 3669]魔法森林(LCT)

来源:互联网 发布:淘宝搞笑收件人名字男 编辑:程序博客网 时间:2024/06/05 18:34

题目链接

http://www.lydsy.com/JudgeOnline/problem.php?id=3669

思路

这个题本来是个SPFA的题。。。但是正解不好想,倒是LCT的暴力比较好想。。。
用Kruscal的方法时刻维护一个生成树,按照a升序加边,并用LCT维护这个生成树,具体做法就是把每个边看成是LCT的结点,点也看成是LCT的结点,加入一条边时,就link一个点和边,link另一个点和边,但是与Kruscal不同的是,如果当前的边的两个端点已经联通了,但是这两个端点路径上的b最大的边比当前的边的b值大,仍然可以加入这条边,并把原来两个端点路径上b最大的那条边cut掉,在上述过程中,如果任何时候发现起点和终点已经联通,就更新答案。。。

代码

其实我只用了半个小时打好LCT,结果一直过不了样例,以为LCT写错了。。。搞了半天才发现LCT没问题,是我先对LCT结点标记初始化了,再给边排的序。。。我靠。。。

#include <iostream>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <algorithm>#define MAXN 200000#define lson ch[o][0]#define rson ch[o][1]using namespace std;int ch[MAXN][2],fa[MAXN],maxtag[MAXN],val[MAXN]; //maxtag[i]=区间最大值的对应子树根节点编号!!!int stack[MAXN],top=0;bool rev[MAXN];bool isRoot(int o){    return ch[fa[o]][0]!=o&&ch[fa[o]][1]!=o;}void pushup(int o){    maxtag[o]=o;    if(val[maxtag[lson]]>val[maxtag[o]]) maxtag[o]=maxtag[lson];    if(val[maxtag[rson]]>val[maxtag[o]]) maxtag[o]=maxtag[rson];}void pushdown(int o){    //if(!o) return;    if(rev[o])    {        rev[lson]^=1;        rev[rson]^=1;        swap(lson,rson);        rev[o]=false;    }}void rot(int x){    int y=fa[x],z=fa[y];    int p,q;    if(ch[y][0]==x) p=0;    else p=1;    q=p^1;    if(!isRoot(y))    {        if(ch[z][0]==y) ch[z][0]=x;        else ch[z][1]=x;    }    fa[x]=z,fa[y]=x;    ch[y][p]=ch[x][q];    fa[ch[x][q]]=y;    ch[x][q]=y;    pushup(y);    pushup(x);}void splay(int x){    top=0;    stack[++top]=x;    for(int i=x;!isRoot(i);i=fa[i]) stack[++top]=fa[i];    for(int i=top;i>=1;i--) pushdown(stack[i]); //!!!!!!    while(!isRoot(x))    {        int y=fa[x],z=fa[y];        if(!isRoot(y))        {            if((ch[y][0]==x)==(ch[z][0]==y)) rot(y);            else rot(x);        }        rot(x);    }}void access(int x){    int tmp=0;    while(x)    {        splay(x);        ch[x][1]=tmp;        pushup(x);        tmp=x;        x=fa[x];    }}void makeroot(int x){    access(x);    splay(x);    rev[x]^=1;}void link(int x,int y) //y是x父亲{    makeroot(x);    fa[x]=y;}void cut(int x,int y) //y是x父亲{    makeroot(x);    access(y);    splay(y);    if(ch[y][0]==x) ch[y][0]=fa[x]=0;}int query(int x,int y) //询问路径xy上的最大边{    makeroot(x);    access(y);    splay(y);    return maxtag[y];}int f[MAXN];int findSet(int x){    if(f[x]==x) return f[x];    return f[x]=findSet(f[x]);}struct edge{    int u,v,a,b;}edges[MAXN];int n,m;bool cmp(edge a,edge b){    return a.a<b.a;}void solve(int i) //在已经联通的块中加入边i{    int old=query(edges[i].u,edges[i].v);    if(val[old]>edges[i].b)    {        cut(edges[old-n].u,old);        cut(edges[old-n].v,old);        link(edges[i].u,n+i);        link(edges[i].v,n+i);    }}void work(){    for(int i=0;i<MAXN;i++) f[i]=i;    int ans=0x3f3f3f3f;    for(int i=1;i<=m;i++)    {        int rootu=findSet(edges[i].u),rootv=findSet(edges[i].v);        if(rootu!=rootv)        {            f[rootu]=rootv;            link(edges[i].u,n+i);            link(edges[i].v,n+i);        }        else solve(i);        if(findSet(1)==findSet(n)) //起点和终点已经在MST中联通            ans=min(ans,val[query(1,n)]+edges[i].a);    }    if(ans!=0x3f3f3f3f) printf("%d\n",ans);    else printf("-1\n");}int main(){    scanf("%d%d",&n,&m);    for(int i=1;i<=m;i++)        scanf("%d%d%d%d",&edges[i].u,&edges[i].v,&edges[i].a,&edges[i].b);    sort(edges+1,edges+m+1,cmp); //!!!!!fuck!!!!!    for(int i=1;i<=m;i++)    {        val[n+i]=edges[i].b;        maxtag[n+i]=n+i;    }    work();    return 0;}
0 0
原创粉丝点击