bzoj 3669 [Noi2014]魔法森林

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3669

第二道LCT!

直接看TJ……https://blog.csdn.net/clove_unique/article/details/51317842

把连边表示成与点相连 是很好的。因为splay中的边变动得挺随意的。

感觉理解又加深了。那个query的部分,效果是x和y所在的辅助树中x深度最小、y深度最大,即当前辅助树就是x和y之间的链!

因为忘记在splay的最后pshp,RE(为什么是RE?)了好久。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=5e4+5,M=1e5+5,INF=0x7fffffff;
int n,m,pre[N+M],mx[N+M],c[N+M][2],fa[N],stack[N+M],top,val[N+M],ans;//val    //
bool rev[N+M];
struct Ed{
    int x,y,a,b;
    bool operator<(const Ed &k)const
        {return a<k.a;}
}ed[M];
int find(int a){return fa[a]==a?a:fa[a]=find(fa[a]);}
bool isroot(int x){return c[pre[x]][0]!=x&&c[pre[x]][1]!=x;}
void pshp(int x)
{
    mx[x]=x;
    if(val[mx[c[x][0]]]>val[mx[x]])mx[x]=mx[c[x][0]];
    if(val[mx[c[x][1]]]>val[mx[x]])mx[x]=mx[c[x][1]];
}
void reverse(int x)
{
    if(rev[x]){
        rev[c[x][0]]^=1;rev[c[x][1]]^=1;
        swap(c[x][0],c[x][1]);
        rev[x]=0;
    }
}
void rotate(int x)
{
    int y=pre[x],z=pre[y],d=(x==c[y][1]);
    if(!isroot(y))c[z][y==c[z][1]]=x;
    pre[x]=z;pre[y]=x;
    c[y][d]=c[x][!d];pre[c[x][!d]]=y;c[x][!d]=y;
    pshp(y);pshp(x);
}
void splay(int x)
{
    stack[top=1]=x;
    for(int t=x;!isroot(t);t=pre[t])stack[++top]=pre[t];
    for(;top;top--)reverse(stack[top]);
    for(;!isroot(x);rotate(x))
    {
        int y=pre[x],z=pre[y];
        if(isroot(y))continue;
        ((c[y][0]==x)^(c[z][0]==y))?rotate(x):rotate(y);
    }
    pshp(x);//////////
}
void access(int x)
{
    for(int t=0;x;c[x][1]=t,t=x,x=pre[x])splay(x);
}
void makeroot(int x)
{
    access(x);splay(x);rev[x]^=1;
}
int query(int x,int y)
{
    makeroot(x);access(y);splay(y);//y没有右儿子     //access之后相当于在辅助树中splay,故不用rev 
//    printf("x=%d y=%d c[x][0]=%d c[x][1]=%d c[y][0]=%d c[y][1]=%d\n",x,y,c[x][0],c[x][1],c[y][0],c[y][1]);
    return mx[y];    //x和y之间的不应该是x的右儿子?
//    int vmx=mx[c[x][1]];    //此时应该是y为根、无右儿子;x为y的左儿子,无左儿子。故。 
//    if(val[vmx]<val[x])vmx=x;    //但为什么这样写不行? 
//    if(val[vmx]<val[y])vmx=y;    //y的左儿子可能不是x。在access的splay中,x可能在自己的辅助树中被转走 
//    return vmx;    //但makert(x)使x变成深度最小的,access(y)使y变成深度最大的,故此时辅助树中只有链上的。 
}
void link(int x,int y)
{
    makeroot(x);pre[x]=y;//此时不用pshp--query的时候会弄好 
}
void cut(int x,int y)
{
    query(x,y);
    pre[x]=0;c[y][0]=0;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d%d%d%d",&ed[i].x,&ed[i].y,&ed[i].a,&ed[i].b);
    sort(ed+1,ed+m+1);
    for(int i=1;i<=n;i++)fa[i]=i;
    ans=INF;
    for(int i=1;i<=m;i++)
    {
        int u=ed[i].x,v=ed[i].y;val[i+n]=ed[i].b;//val应记录排序后的 
        if(find(u)!=find(v)){
            fa[find(u)]=find(v);
            link(i+n,u);link(i+n,v);
            if(find(1)==find(n))ans=min(ans,ed[i].a+val[query(1,n)]);
        }
        else{
            query(u,v);int k=mx[v];if(val[k]<val[i+n])continue;//
            cut(k,ed[k-n].x);cut(k,ed[k-n].y);
            link(i+n,u);link(i+n,v);
            if(find(1)==find(n))ans=min(ans,ed[i].a+val[query(1,n)]);    //这里+val[query(1,n)],不是val[i+n] 
        }
    }
    printf("%d",(ans==INF?-1:ans));
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Narh/p/9234470.html