[NOI2014][JZOJ3754][BZOJ3669]魔法森林

来源:互联网 发布:云计算的部署方式 编辑:程序博客网 时间:2024/05/20 10:13

题目大意

给定一个n个点m条边的无向图。每条边有两个权值(ai,bi)
你需要找到一条从1n的路径,使得路径上ai最大值与bi最大值的和尽量小。

2n5×104,0m105,1ai,bi5×104


题目分析

这题看上去就很mst套路,实际上也是mst套路。
考虑从小到大枚举路径上ai的最大值,然后加入满足条件的边。
如果形成了环就删掉环上bi最大的边。如果1n是联通的就更新答案。
使用LCT维护森林,使用并查集维护连通性。
时间复杂度O(nlogn+nα(n))


代码实现

人生第一发lct居然很快就调过了,exciting!

#include <algorithm>#include <iostream>#include <cstdio>#include <cctype>#include <stack>using namespace std;int read(){    int x=0,f=1;    char ch=getchar();    while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();    return x*f;}const int INF=1000000000;const int N=50005;const int M=100005;const int V=N+M;struct edge{    int x,y,a,b;    bool operator<(edge const ed)const{return a<ed.a;}    void load(){x=read(),y=read(),a=read(),b=read();}}edg[M];int fa[N],rank[N];int n,m,ans;struct link_cut_tree{    int par[V],fa[V],size[V],id[V],mx[V];    int son[V][2];    stack<int> st;    bool mark[V];    bool side(int x){return son[fa[x]][1]==x;}    void R(int x){swap(son[x][0],son[x][1]),mark[x]^=1;}    void clear(int x)    {        if (mark[x])        {            if (son[x][0]) R(son[x][0]);            if (son[x][1]) R(son[x][1]);            mark[x]=0;        }    }    void update(int x)    {        size[x]=size[son[x][0]]+size[son[x][1]]+1;        mx[x]=edg[mx[son[x][0]]].b>edg[mx[son[x][1]]].b?mx[son[x][0]]:mx[son[x][1]];        mx[x]=edg[id[x]].b>edg[mx[x]].b?id[x]:mx[x];    }    void pushdown(int x,int y)    {        for (;x!=y;st.push(x),x=fa[x]);        for (;!st.empty();clear(st.top()),st.pop());    }    void rotate(int x)    {        int y=fa[x];bool s=side(x);        if (fa[y]) son[fa[y]][side(y)]=x;        if (son[x][s^1]) fa[son[x][s^1]]=y;        son[y][s]=son[x][s^1],son[x][s^1]=y;        fa[x]=fa[y],fa[y]=x;        if (par[y]) par[x]=par[y],par[y]=0;        update(y),update(x);    }    void splay(int x,int y)    {        for (pushdown(x,y);fa[x]!=y;rotate(x))            if (fa[fa[x]]!=y)                if (side(x)==side(fa[x])) rotate(fa[x]);                else rotate(x);    }    int access(int x)    {        int nxt=0;        for (;x;update(nxt=x),x=par[x])        {            splay(x,0);            if (son[x][1]) fa[son[x][1]]=0,par[son[x][1]]=x;            son[x][1]=nxt;            if (nxt) fa[nxt]=x,par[nxt]=0;        }        return nxt;    }    void makeroot(int x){R(access(x));}    void link(int x,int y)    {        makeroot(x),access(x),splay(x,0);        par[x]=y,access(x);    }    void cut(int x,int y)    {        makeroot(x),access(y),splay(y,0);        fa[x]=son[y][0]=0,par[y]=0,update(y);    }    int query(int x,int y)    {        makeroot(x),access(y),splay(y,0);        return mx[y];    }}lct;int getfather(int son){return fa[son]==son?son:fa[son]=getfather(fa[son]);}void merge(int x,int y){    if (rank[x]>rank[y]) swap(x,y);    fa[y]=x,rank[y]+=rank[x]==rank[y];}void calc(){    for (int i=1;i<=n;++i) fa[i]=i;    sort(edg+1,edg+1+m),ans=INF;    for (int i=1;i<=n+m;++i) lct.size[i]=1;    for (int i=1;i<=m;++i)    {        int x=edg[i].x,y=edg[i].y,fx=getfather(x),fy=getfather(y);        if (fx!=fy) merge(fx,fy),lct.mx[i+n]=lct.id[i+n]=i,lct.link(x,i+n),lct.link(i+n,y);        else        {            int id=lct.query(x,y);            if (edg[id].b>edg[i].b)            {                int u=edg[id].x,v=edg[id].y;                lct.cut(u,id+n),lct.cut(id+n,v);                lct.mx[i+n]=lct.id[i+n]=i,lct.link(x,i+n),lct.link(i+n,y);            }        }        if (getfather(1)==getfather(n)) ans=min(ans,edg[i].a+edg[lct.query(1,n)].b);    }}int main(){    freopen("forest.in","r",stdin),freopen("forest.out","w",stdout);    n=read(),m=read();    for (int i=1;i<=m;++i) edg[i].load();    calc();    if (ans==INF) printf("-1\n");    else printf("%d\n",ans);    fclose(stdin),fclose(stdout);    return 0;}
0 0