BZOJ 3669 [Noi2014]魔法森林 LCT

題目大意:給出一個n 個點m 條邊的無向圖,每條邊上有兩個權值ai ,bi ,要求選擇一些邊使1,n 連通,問這些邊中兩個權值分別取最大後求和最小是多少

考試的時候只想到按a排序後二分b…

考慮只有一種權值的情況就是做一遍類似最小生成樹的東西,按權值排序後按順序不斷加邊直到1,n連通

兩個權值的話考慮去掉一個,於是可以枚舉a,剩下一個權值b

不過權值b是無序的。a已經不用考慮了,現在的目標是使1到n的路徑上的b的最大值儘量小。可以用LCT來維護一個生成樹,將邊變爲點。加邊時若兩個點不連通則連接,否則找出鏈上最大值,若當前邊更優則斷掉最大後連邊。然後按當前情況更新下答案就好

#include <cstdio>
#include <algorithm>
#define N 50005
#define INF 1000000000
using namespace std;
struct Edge {
    int u,v,a,b;
    void scan() { scanf("%d%d%d%d",&u,&v,&a,&b); }
    bool operator < (const Edge& rhs) const {
        return a<rhs.a;
    }
}edges[N*2];
struct Node {
    Node *pa,*ch[2];
    int val,maxx;
    bool rev_mark;
    Node();
    int dir();
    void rev();
    void pushdown();
    void maintain();
}*null=new Node(),p[N],e[N*2];
Node::Node() {
    val=maxx=0;
    pa=ch[0]=ch[1]=null;
    rev_mark=false;
}
int Node::dir() { return pa->ch[0]==this ? 0 : pa->ch[1]==this ? 1 : -1; }
void Node::rev() {
    rev_mark=!rev_mark;
    swap(ch[0],ch[1]);
    return ;
}
void Node::pushdown() {
    if(rev_mark) {
        ch[0]->rev();
        ch[1]->rev();
        rev_mark=false;
    }
    return ;
}
void Node::maintain() {
    maxx=val;
    if(edges[ch[0]->maxx].b>edges[maxx].b) maxx=ch[0]->maxx;
    if(edges[ch[1]->maxx].b>edges[maxx].b) maxx=ch[1]->maxx;
    return ;
}
void Rotate(Node* o,int d) {
    Node* k=o->ch[d^1]; int d2;
    o->ch[d^1]=k->ch[d]; k->ch[d]->pa=o;
    k->ch[d]=o;
    o->maintain(), k->maintain();
    if(~(d2=o->dir())) o->pa->ch[d2]=k;
    k->pa=o->pa; o->pa=k;
    return ;
}
void To_pushdown(Node* o) {
    static Node* tmp[N];
    int top=0;
    while(o!=null) tmp[++top]=o, o=o->pa;
    while(top) tmp[top--]->pushdown();
    return ;
}
void Splay(Node* o) {
    int d;
    To_pushdown(o);
    while(~(d=o->dir())) {
        if(o->pa->dir()==d) Rotate(o->pa->pa,d^1);
        Rotate(o->pa,d^1);
    }
    return ;
}
void Access(Node* o) {
    Node* p=null;
    while(o!=null) {
        Splay(o);
        o->ch[1]=p, o->maintain();
        p=o;
        o=o->pa;
    }
    return ;
}
void Move_to_root(Node* o) {
    Access(o), Splay(o);
    o->rev();
    return ;
}
void Link(Node* x,Node* y) {
    Move_to_root(x);
    x->pa=y;
    return ;
}
void Cut(Node* x,Node* y) {
    Move_to_root(x);
    Access(y), Splay(y);
    y->ch[0]=null, y->maintain();
    x->pa=null;
    return ;
}
int Query_max(Node* x,Node* y) {
    Move_to_root(x);
    Access(y), Splay(y);
    return y->maxx;
}
int n,m,ans=INF,pa[N],siz[N];
int root(int x) { return pa[x]==x ? x : pa[x]=root(pa[x]); }
int main() {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i) edges[i].scan();
    sort(edges+1,edges+m+1);
    for(int i=1;i<=m;++i) e[i].val=e[i].maxx=i;
    for(int i=1;i<=n;++i) pa[i]=i, siz[i]=1;
    for(int i=1;i<=m;++i) {
        int pa_u=root(edges[i].u),pa_v=root(edges[i].v);
        if(pa_u!=pa_v) {
            Link(p+edges[i].u,e+i);
            Link(p+edges[i].v,e+i);
            if(siz[pa_u]>siz[pa_v]) swap(pa_u,pa_v);
            pa[pa_u]=pa_v;
            siz[pa_v]+=siz[pa_u];
        }
        else {
            int tmp=Query_max(p+edges[i].u,p+edges[i].v);
            if(edges[tmp].b>edges[i].b) {
                Cut(p+edges[tmp].u,e+tmp);
                Cut(p+edges[tmp].v,e+tmp);
                Link(p+edges[i].u,e+i);
                Link(p+edges[i].v,e+i);
            }
        }
        int pa_st=root(1),pa_ed=root(n);
        if(pa_st==pa_ed) {
            int tmp=Query_max(p+1,p+n);
            ans=min(ans,edges[i].a+edges[tmp].b);
        }
    }
    if(ans==INF) ans=-1;
    printf("%d\n",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章