题目描述:https://www.luogu.org/problem/P2387

 

解析:题目要求的是最大值最小,首先想到二分,但有两个变量不好搞,于是想到一个显然的贪心:对于两个点u.v,他们之间的边a值小,b值也小的边肯定更优。所以我们先将边按a值排序,然后按b值来维护最小生成树。对于一条新插入的边,如果它已经出现在了这颗最小生成树中,那么我们查询这个环中最大的边是否比这个边大,如果是,那么断掉原来的那条边。那么我么怎样维护这颗最小生成树呢?显然就是LCT了。但我们要维护的是边值,怎么搞呢?一个套路就是多建一个点,然后将这个点的值赋为边权,起点与终点分别连向这条边即可。

 

细节:1.没什么细节,不要写挂就好。

附上代码:

#include<cstdio>
#include<iostream>
#include<algorithm> 
using namespace std;

//给定一个图,每个边有两个权值a.b,求起点到给定点的一条路径
//使得这条路径上a的最大值与b的最大值的和最小 

const int MAXN=50005,MAXM=200005;
int n,m;
struct Edge{
    int u,v,a,b;
}edge[MAXM];
int fa[MAXM];
struct Node{
    int max,val,son[2],fa;
    bool flag_reverse;
}node[MAXM<<1];
int ans=0x3f3f3f;
int stk[MAXM<<1];

inline int read(){
    int ret=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') f=-f;c=getchar();}
    while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
    return ret*f;
}

void readIn(int i){
    edge[i].u=read();edge[i].v=read();edge[i].a=read();edge[i].b=read();
    edge[i+1].u=edge[i].u;edge[i+1].v=edge[i].v;edge[i+1].a=edge[i].a;edge[i+1].b=edge[i].b;
}

int union_find(int x){
    if(x==fa[x]) return x;
    else return fa[x]=union_find(fa[x]);
}

void update(int x){
    node[x].max=x;
    int lson=node[x].son[0],rson=node[x].son[1];
    if(lson&&node[node[lson].max].val>node[node[x].max].val) node[x].max=node[lson].max;
    if(rson&&node[node[rson].max].val>node[node[x].max].val) node[x].max=node[rson].max;
}

void pushdown(int x){
    if(node[x].flag_reverse){
        int lson=node[x].son[0],rson=node[x].son[1];
        node[lson].flag_reverse^=1;
        node[rson].flag_reverse^=1;
        node[x].flag_reverse^=1;
        swap(node[x].son[0],node[x].son[1]);        
    } 
}

int get(int x){
    return x==node[node[x].fa].son[0]||x==node[node[x].fa].son[1];
}

int check(int x){
    return x==node[node[x].fa].son[1];
}

void rotate(int x){
    int y=node[x].fa,z=node[y].fa,d=check(x),xx=node[x].son[d^1];
    node[y].son[d]=xx;if(xx) node[xx].fa=y;
    if(get(y)) node[z].son[check(y)]=x;node[x].fa=z;
    node[x].son[d^1]=y;node[y].fa=x;
    update(y);update(x);
}

void splay(int x){
    int y=x,top=0;stk[++top]=y;
    while(get(y)) stk[++top]=y=node[y].fa;
    while(top) pushdown(stk[top--]);
    while(get(x)){
        int y=node[x].fa;
        if(get(y))
            rotate(check(x)==check(y)?y:x);
        rotate(x);
    }
} 

void access(int x){
    int xx=0;
    while(x){
        splay(x);
        node[x].son[1]=xx;
        xx=x;
        update(x);
        x=node[x].fa;
    }
}

int find(int x){
    access(x);splay(x);
    while(node[x].son[0]) x=node[x].son[0];
    return x;
}

void make_root(int x){
    access(x);splay(x);
    node[x].flag_reverse^=1;
}

void link(int x,int y){
    make_root(x);if(find(y)!=x) node[x].fa=y;
}

void split(int x,int y){
    make_root(x);access(y);splay(y);
}

void cut(int x,int y){
    split(x,y);
    if(find(y)==x&&!node[x].son[1]&&node[x].fa==y)
        node[x].fa=node[y].son[0]=0;
}

int query(int x,int y){
    split(x,y);
    return node[y].max;
}

void work(){
    for(int i=1;i<=m;++i) fa[i]=i;
    for(int i=1;i<=m;i++){
        int u=edge[i].u,v=edge[i].v;
        if(union_find(u)==union_find(v)){
            int t=query(u,v);
            if(edge[i].b<node[t].val){
                cut(t,edge[t-n].u);
                cut(t,edge[t-n].v);
            }
            else{
                if(union_find(1)==union_find(n))
                    ans=min(ans,edge[i].a+node[query(1,n)].val);
                continue;
            }
        }
        else fa[union_find(u)]=union_find(v);
        node[i+n].max=i+n;node[i+n].val=edge[i].b;
        link(u,i+n);link(i+n,v);
        if(union_find(1)==union_find(n))
            ans=min(ans,edge[i].a+node[query(1,n)].val);
    }
    if(ans!=0x3f3f3f)
        printf("%d",ans);
    else 
        printf("-1"); 
}

bool cmp(Edge A,Edge B){
    return A.a<B.a;
}

int main(){
    n=read();m=read();m<<=1;
    for(int i=1;i<=m;i+=2) readIn(i);
    sort(edge+1,edge+m+1,cmp);
    work();
    return 0;
}
View Code