NOI2014 魔法森林 LCT 动态维护 MST

和货车运输那道题挺像的。

运用到一个结论:图上两点间路径边权最大值的最小值存在于整个图的最小生成树中。

有了这个结论,我们就要将问题向最小生成树上靠拢。

我们可以将 $c$ 从小到大排序,依次枚举。

这其实是在构建以 $e$ 为关键字的最小生成树。

动态加入对应的边,用 $LCT$ 维护最小生成树即可。

如果两点不联通,这个边显然是要加的。

如果两点联通,考虑一下两种情况:

1)新加入的 $e$ 值大于两点简单路径间最大值,显然不要加( $c$ 是不下降的)

2)新加入的 $e$ 值小于两点简单路径间最大值,是要加入的。

(由于我们研究的是 $1$ 到 $n$ 的路径,每次都 Acess 查一下答案即可)

即使加入了更差,那也只可能是最后一条边会有这样的后果(想一想,为什么)

Code:

#include <cstdio>
#include <algorithm>
#include <cstring>
#define maxn 1000000
#define ll long long 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;

int n,m,tot,err[maxn]; 
struct E{ int son,fa,c;   }trash[maxn << 1]; 
struct Edge{ int u,v,c,e;   }A[maxn],edges[maxn]; 
int cmp(Edge a,Edge b){ return a.c < b.c; }
int p[maxn];
void init(){ for(int i=0;i<maxn;++i) p[i]=i;}
int find(int x) { return p[x]==x?x:p[x]=find(p[x]);}
void merge(int x,int y) { int a=find(x),b=find(y); p[a] = b; }

int ch[maxn][2],f[maxn],maxv[maxn],val[maxn],posv[maxn],stac[maxn],tag[maxn]; 
int lson(int x){ return ch[x][0]; }
int rson(int x){ return ch[x][1]; }
int isRoot(int x){ return (ch[f[x]][1]!=x && ch[f[x]][0]!=x);}
int get(int x){ return ch[f[x]][1]==x;}
void make(int x) { if(x)tag[x]^=1,swap(ch[x][0],ch[x][1]);}
void pushup(int x){
    maxv[x]=max(maxv[lson(x)],maxv[rson(x)]); 
    posv[x]=maxv[lson(x)] > maxv[rson(x)] ? posv[lson(x)] : posv[rson(x)]; 
    if(val[x]>maxv[x]) maxv[x]=val[x],posv[x]=x;  
}
void pushdown(int x){ if(tag[x] && x)make(lson(x)),make(rson(x)),tag[x]^=1; }
void rotate(int x){
    int old=f[x],oldf=f[old],which=get(x); 
    if(!isRoot(old)) ch[oldf][ch[oldf][1]==old]=x; 
    ch[old][which]=ch[x][which^1],f[ch[old][which]]=old; 
    ch[x][which^1]=old,f[old]=x,f[x]=oldf; 
    pushup(old),pushup(x); 
} 
void splay(int x){ 
    int v=0,u=x;
    stac[++v]=u;
    while(!isRoot(u)) stac[++v]=f[u],u=f[u];
    while(v) pushdown(stac[v--]);
    u=f[u];
    for(int fa;(fa=f[x])!=u;rotate(x))
        if(f[fa]!=u) rotate(get(fa)==get(x)?fa:x);
}
void Access(int x) {for(int y=0;x;y=x,x=f[x]) splay(x),ch[x][1]=y,pushup(x);} 
void makeRoot(int x){Access(x),splay(x),make(x);} 
void link(int x,int y) {makeRoot(x),f[x]=y;}
void split(int x,int y) {makeRoot(x),Access(y),splay(y);}  
void cut(int x,int y){makeRoot(x),Access(y),splay(y),ch[y][0]=f[ch[y][0]]=0,pushup(y);}
int main(){
    //setIO("input"); 
    init(); 
    scanf("%d%d",&n,&m);
    int cc=0; 
    for(int i=1;i<=m;++i) {
        scanf("%d%d%d%d",&A[i].u,&A[i].v,&A[i].c,&A[i].e); 
        if(A[i].u!=A[i].v) edges[++cc]=A[i]; 
    }
    m=cc; 
    sort(edges+1,edges+1+m,cmp);
    
    int maxc=0,maxe=0; 
    tot=n+1; 
    int cnt=0,ans=2e9; 
    for(int i=1;i<=m;++i) {
        int a=edges[i].u,b=edges[i].v;  
        if(find(a)==find(b)) {
            split(a,b); 
            if(edges[i].e  < maxv[b] ) {       
                int w=posv[b]; 
                cut(w,trash[w].fa); 
                cut(trash[w].son,w); 
                err[w]=0; 
                ++tot; 
                val[tot]=maxv[tot]=edges[i].e;
                posv[tot]=tot; 
                trash[tot].fa=b;
                trash[tot].son=a;
                trash[tot].c=edges[i].c; 
                err[tot]=edges[i].e; 
                link(tot,a),link(b,tot);  
                maxc=max(maxc,edges[i].c); 
            }
        }
        else {
            ++cnt; 
            merge(a,b); 
            ++tot; 
            val[tot]=maxv[tot]=edges[i].e; 
            trash[tot].fa=b;
            trash[tot].son=a;
            trash[tot].c=edges[i].c; 
            posv[tot]=tot; 
            err[tot]=edges[i].e; 
            link(a,tot),link(tot,b); 
            maxc=max(maxc,edges[i].c); 
        }
        if(find(1)==find(n)) {
            split(1,n); 
            ans=min(ans,maxc+maxv[n]); 
        }
    }
    for(int i=1;i<=tot;++i) maxe=max(maxe,err[i]); 
    if(find(1)!=find(n)) printf("-1"); 
    else printf("%d",ans); 
    return 0; 
}

  

posted @ 2019-02-17 22:02  EM-LGH  阅读(331)  评论(0)    收藏  举报