BZOJ 3669: [Noi2014]魔法森林

题目

BZOJ 3669: [Noi2014]魔法森林

题解

感觉收获就是get了一种除了Kruskal和prim之外的最小生成树算法。
理论复杂度貌似是mlogn,不过常数巨大堪比一个log(此句话引用自某论文)。

陈年老题谁都会做,我没做出来qwq。边权按a排序之后做b的最小生成树。对于一条连接(u,v)的边,如果uv不连通那么连起来,否则找u->v最大的边权,然后切掉,把当前边加进去,当1,n联通的时候可以更新答案(我tm还zz的写成了弄出来一棵生成树的时候才有答案,好像也有很多人和我一样(雾))。边权不好维护,把边抽象成点,用点权维护。

然后就是splay维护最大值和最大值出现位置了。easy。感觉lct的板子总是哪里要打错然后调很久233,。不过调的越来越熟练了。自己发明了一套成体系的调试方法,对于指针党简直是太棒了。

代码

//QWsin
#include<map>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int maxn=200000+10;
const int maxm=100000+10;

map<int,int>id;

int n,m;
struct Edge
{
    int u,v,a,b;
    inline void input(){scanf("%d%d%d%d",&u,&v,&a,&b);}
    inline bool operator < (const Edge &rhs)const{
        return a<rhs.a||(a==rhs.a&&b<rhs.b);
    }
} e[maxm];

struct Node
{
    int v,pos,mx,posx,rev;
    Node *ch[2],*fa;
    Node(){v=pos=mx=posx=rev=0;ch[0]=ch[1]=fa=NULL;}
    Node(int v,int pos):v(v),pos(pos){mx=v,posx=pos;rev=0;ch[0]=ch[1]=fa=NULL;}
    inline void push(){
        if(rev){
            swap(ch[0],ch[1]);
            if(ch[0]) ch[0]->rev^=1;
            if(ch[1]) ch[1]->rev^=1;
            rev=0;
        }
    }
    inline void up()
    {
        mx=v,posx=pos;
        if(ch[0] && ch[0]->mx > mx) mx=ch[0]->mx,posx=ch[0]->posx;
        if(ch[1] && ch[1]->mx > mx) mx=ch[1]->mx,posx=ch[1]->posx;
    };

    inline void out(){
        if(!this) {puts("NULL");return ;}
        printf("%d:\n",id[(int)this]);
        printf("    ch[0]=%d\n",id[(int)ch[0]]);
        printf("    ch[1]=%d\n",id[(int)ch[1]]);
        printf("    fa=%d\n",id[(int)fa]);
        printf("    val=%d\n",v);
        printf("    mx=%d\n",mx);
        printf("    posx=%d\n",posx);
        puts("\n");
    }
}*tmp[maxn];

struct LinkCutTree
{

    Node *node[maxn];
    inline void init()
    {
        id[0]=0;
        for(int i=1; i<=n; ++i) node[i]=new Node(),id[(int)node[i]]=i;
        for(int i=1; i<=m; ++i) node[n+i]=new Node(e[i].b,i),id[(int)node[n+i]]=n+i;
    }

    inline int pd(Node* p){return p->fa->ch[1]==p;}
    inline int is_root(Node* p){return (!(p->fa))||(p->fa->ch[0]!=p&&p->fa->ch[1]!=p);}
    inline void rotate(Node* p)
    {
        int c=pd(p)^1;
        Node *t=p->fa;
        t->ch[c^1]=p->ch[c];
        if(p->ch[c]) p->ch[c]->fa=t;
        p->fa=t->fa;
        if(!is_root(t)) p->fa->ch[pd(t)]=p;
        t->fa=p;p->ch[c]=t;t->up();p->up();
//      p->out();
    }

    inline void splay(Node *p)
    {
        int top=0;
        for(Node* t=p;; t=t->fa)
        {
            tmp[++top]=t;
            if(is_root(t)) break;
        }
        for(; top>=1; --top) tmp[top]->push();
        for(; !is_root(p); rotate(p))
            if(!is_root(p->fa)) rotate(pd(p)==pd(p->fa) ? p->fa:p);
    }

    inline void access(Node* p)
    {
//      p->out();
        for(Node* pre=NULL; p ; pre=p,p=p->fa){
            splay(p),p->ch[1]=pre,p->up();
//          pre->out();p->out();
        }
    }

    inline void make_root(Node* p){access(p);splay(p);p->rev=1;}
    inline void link(Node* a,Node *b){
//      a->out();b->out();
        make_root(a);a->fa=b;
//      a->out();b->out();
    }

    inline void cut(Node* a,Node *b){
        make_root(a);access(b);splay(b);
        a->fa=b->ch[1]=NULL;b->up();
    }

    inline void link(int i){
        link(node[e[i].u],node[n+i]);
        link(node[n+i],node[e[i].v]);
    }

    inline void cut(int i){
        cut(node[e[i].u],node[i+n]);
        cut(node[i+n],node[e[i].v]);
    }

    inline int getmx(int u,int v,int &id) //u->v路径最大值
    {
        make_root(node[v]);
        access(node[u]);
        splay(node[u]);
        id=node[u]->posx;
        return node[u]->mx;
    }

    //在u->v路径上找到最大的决定要不要换
    inline void find_mx(int i)
    {
        int id,val=getmx(e[i].u,e[i].v,id);
        if(val > e[i].b){cut(id);link(i);}
    }
} lct;

inline void init_data()
{
    cin>>n>>m;
    for(int i=1; i<=m; ++i) e[i].input();
    sort(e+1,e+m+1);
}

int p[maxn];
int findset(int x){return p[x]==x?x:p[x]=findset(p[x]);}

inline void solve()
{
    for(int i=1; i<=n; ++i) p[i]=i;
    lct.init();

    int ans=1<<30,cnt=n,bin;
    for(int i=1; i<=m; ++i)
    {
        if(e[i].a > ans) break;
        int u=e[i].u,v=e[i].v;
        u=findset(u);
        v=findset(v);
        if(u!=v){lct.link(i); p[u]=v; --cnt;}
        else  lct.find_mx(i);
        if(findset(1)==findset(n)) 
            ans=min(ans,e[i].a+lct.getmx(1,n,bin));
    }
    if(findset(1)!=findset(n)) puts("-1");
    else cout<<ans<<endl;
}

int main()
{
    init_data();
    solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值