BZOJ 3669: [Noi2014]魔法森林

来源:互联网 发布:农村淘宝咨询电话 编辑:程序博客网 时间:2024/05/17 16:00

题目

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;}