NOI2014 魔法森林 LCT维护MST

BZOJ3669 题面


从更简单的情况入手,如果边权只有 a 没有 b 应该怎么处理?这时候问题就是找一条从1到N的路径,使得最长的边尽量短。根据最小生成树的性质,这样的边一定在最小生成树上。

如果 a 固定,得到的解法是一样的。那么可以分别讨论每一个 a ,对于权值不大于 a 的边对 b 做一次最小生成树。暴力做会超时。考虑到随着 a 的变大,可用的边也逐渐增多,那么只要在加边的同时更新最小生成树就可以了。这个操作可以用LCT处理。


#include
#include
#define MAXN 150005
#define MAXM 200005
using namespace std;

int N,M,Ans=1e9;

struct node{int st,en,va,vb;}edge[MAXM];
bool operator<(node x,node y)
{
    if(x.va==y.va)return x.vbreturn x.vaint ls[MAXN],rs[MAXN],fa[MAXN],rev[MAXN],val[MAXN];
int Max[MAXN],id[MAXN];

bool isrt(int x){return ls[fa[x]]!=x&&rs[fa[x]]!=x;}

void Update(int p)
{
    if(Max[ls[p]]>Max[rs[p]])Max[p]=Max[ls[p]],id[p]=id[ls[p]];
    else Max[p]=Max[rs[p]],id[p]=id[rs[p]];
    if(Max[p]void Putdown(int p)
{
    if(rev[p]==0)return;
    rev[ls[p]]^=1;rev[rs[p]]^=1;rev[p]=0;
    swap(ls[p],rs[p]);
}

void Zig(int x)
{
    int y=fa[x],z=fa[y];
    if(!isrt(y))
    {
        if(ls[z]==y)ls[z]=x;
        else rs[z]=x;
    }
    fa[x]=z;fa[y]=x;fa[rs[x]]=y;
    ls[y]=rs[x];rs[x]=y;
    Update(y);Update(x);
}

void Zag(int x)
{
    int y=fa[x],z=fa[y];
    if(!isrt(y))
    {
        if(ls[z]==y)ls[z]=x;
        else rs[z]=x;
    }
    fa[x]=z;fa[y]=x;fa[ls[x]]=y;
    rs[y]=ls[x];ls[x]=y;
    Update(y);Update(x);
}

int s[MAXN],Top;
void Splay(int x)
{
    int i,y,z;

    s[++Top]=x;
    for(i=x;!isrt(i);i=fa[i])s[++Top]=fa[i];
    while(Top)Putdown(s[Top--]);

    while(!isrt(x))
    {
        y=fa[x];z=fa[y];
        if(isrt(y))
        {
            if(ls[y]==x)Zig(x);
            else Zag(x);
        }
        else
        {
            if(ls[z]==y)
            {
                if(ls[y]==x)Zig(y),Zig(x);
                else Zag(x),Zig(x);
            }
            else
            {
                if(rs[y]==x)Zag(y),Zag(x);
                else Zig(x),Zag(x);
            }
        }
    }
}

void Access(int x)
{
    int t=0;
    while(x)
    {
        Splay(x);
        rs[x]=t;Update(x);
        t=x;x=fa[x];
    }
}

void SetRt(int x)
{
    Access(x);Splay(x);rev[x]^=1;
}

void Link(int x,int y)
{
    SetRt(x);fa[x]=y;
}

void Cut(int x,int y)
{
    SetRt(x);
    Access(y);Splay(y);
    ls[y]=fa[x]=0;
    Update(y);
}

int FindRt(int x)
{
    Access(x);Splay(x);
    while(ls[x])x=ls[x];
    return x;
}

int tot,lk[MAXM][2];
void Add(int x,int y,int v)
{
    tot++;
    lk[tot][0]=x;lk[tot][1]=y;
    val[tot]=v;id[tot]=tot;
    Link(x,tot);Link(tot,y);
}

void Del(int x)
{
    Cut(x,lk[x][0]);Cut(x,lk[x][1]);
}

int GetMax(int x,int y,int op)
{
    SetRt(x);
    Access(y);Splay(y);
    if(op==1)return Max[y];
    else return id[y];
}

int main()
{
    int i,j;

    scanf("%d%d",&N,&M);
    for(i=1;i<=M;i++)scanf("%d%d%d%d",&edge[i].st,&edge[i].en,&edge[i].va,&edge[i].vb);

    sort(edge+1,edge+M+1);

    tot=N;
    for(i=j=1;i<=50000;i++)
    {
        while(j<=M&&edge[j].va<=i)
        {
            int x,y;

            if(edge[j].st==edge[j].en){j++;continue;}

            x=FindRt(edge[j].st);
            y=FindRt(edge[j].en);
            if(x!=y)Add(edge[j].st,edge[j].en,edge[j].vb);
            else
            {
                int tmp=GetMax(edge[j].st,edge[j].en,1);
                if(tmp>edge[j].vb)
                {
                    tmp=GetMax(edge[j].st,edge[j].en,2);
                    Del(tmp);Add(edge[j].st,edge[j].en,edge[j].vb);
                }
            }
            j++;
        }
        if(FindRt(1)!=FindRt(N))continue;
        Ans=min(Ans,i+GetMax(1,N,1));
    }

    if(Ans==1e9)puts("-1");
    else printf("%d\n",Ans);
}

你可能感兴趣的:(LCT,最小生成树)