BZOJ 3669 [Noi2014]魔法森林 LCT

题目大意:给出一个 n 个点m条边的无向图,每条边上有两个权值 ai , bi ,要求选择一些边使1, n <script type="math/tex" id="MathJax-Element-18">n</script>连通,问这些边中两个权值分别取最大后求和最小是多少

考试的时候只想到按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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值