[JZOJ3754]【NOI2014】魔法森林

时间:2023-02-10 17:22:52

题目大意

给定N个点M条边的无向图,每条边有两个权值a与b。求一条1到n的路径使得路径经过边的最大a与最大b的和最小。无法到达输出-1。
n<=50000,m<=100000。ai,bi<=50000

分析

看到ai这么小,我们考虑枚举ai然后算最小b。
那么从小到大枚举ai,不断加入边,边权为bi。
用LCT维护最小生成树即可。具体的,新加入一条边(x,y),若不在同一联通块就link,在同一联通块看看路径上是否有权值比bi大的,找到最大的,cut,再link(x,y)
若1和n在同一联通块,就算1到n简单路径的大权值,加上ai更新答案。
实现技巧:边权不好维护,我们把边拆成一个点两条边,点权为边权,两条边连向原来两点。

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
const int N=200005;
struct rec
{
    int x,y,a,b;
}b[N];
int tr[N][2],tag[N],tmax[N],tval[N],tsiz[N],up[N],tup[N],sta[N],st;
int dad[N],rx,ry;
int xx,yy,ans,pp,x,y,z,i,j,n,m,pos;
bool cmp(rec a,rec b)
{
    return a.a<b.a;
}
int get(int x)
{
    if (dad[x]==x) return x;
    return dad[x]=get(dad[x]);
}
int pd(int x)
{
    return tr[up[x]][1]==x;
}
void down(int x)
{
    if (!tag[x]) return;
    swap(tr[x][0],tr[x][1]);
    tag[tr[x][0]]^=1;
    tag[tr[x][1]]^=1;
    tag[0]=tag[x]=0;
}
void update(int x)
{
    int ls=tr[x][0],rs=tr[x][1];
    tsiz[x]=tsiz[ls]+tsiz[rs]+1;
    if (tval[tmax[ls]]>tval[tmax[rs]]) tmax[x]=tmax[ls];else tmax[x]=tmax[rs];
    if (tval[x]>tval[tmax[x]]) tmax[x]=x;
}
void rotate(int     x)
{
    int y=up[x],z;
    z=pd(x);
    up[x]=up[y];
    if (up[x]) tr[up[x]][pd(y)]=x;
    tr[y][z]=tr[x][1-z];
    if (tr[y][z]) up[tr[y][z]]=y;
    tr[x][1-z]=y;
    up[y]=x;
    update(y);
    update(x);
    if (tup[y]) tup[x]=tup[y];
    tup[y]=0;
}
void remove(int x,int y)
{
    st=1;
    sta[1]=x;
    while (x!=y)
        sta[++st]=(x=up[x]);
    while (st)
        down(sta[st--]);
}
void splay(int x,int y)
{
    remove(x,y);
    while (up[x]!=y)
    {
        if (up[up[x]]!=y)
        {
            if (pd(x)==pd(up[x]))
                rotate(up[x]);
            else rotate(x);
        }
        rotate(x);
    }
}
void access(int x)
{
    int last=0,z=x;
    while (x)
    {
        splay(x,0);
        up[tr[x][1]]=0;
        if (tr[x][1]) tup[tr[x][1]]=x;
        tr[x][1]=last;
        tup[last]=0;
        if (last) 
            up[last]=x;
        update(x);
        last=x;
        x=tup[x];
    }
    splay(z,0);
}
void makeroot(int x)
{
    access(x);
    tag[x]^=1;
}
void cut(int x,int y)
{
    makeroot(x);
    access(y);
    if (tr[y][0]) up[tr[y][0]]=0;
    tr[y][0]=0;
}
void link(int x,int y)
{
    makeroot(x);
    //access(y);
    splay(x,0);
    tup[x]=y;
}
int main()
{
    freopen("3754.in","r",stdin);
    scanf("%d %d",&n,&m);
    fo(i,1,m)
        scanf("%d %d %d %d",&b[i].x,&b[i].y,&b[i].a,&b[i].b);
    sort(b+1,b+1+m,cmp);
    ans=N;
    fo(i,1,n+m) dad[i]=i;
    fo(i,1,m)
    {
        if (b[i].x==b[i].y) continue;
        x=b[i].x;
        y=b[i].y;
        tval[i+n]=b[i].b;
        tmax[i+n]=i+n;
        rx=get(x);
        ry=get(y);
        if (rx!=ry)
        {
            dad[rx]=ry;
            link(i+n,x);
            link(i+n,y);
        }else
        {
            makeroot(x);
            access(y);
            if (tval[tmax[y]]>b[i].b)
            {
                z=tmax[y];
                cut(z,b[z-n].x);
                cut(z,b[z-n].y);
                link(i+n,x);
                link(i+n,y);
            }
        }
        if (get(1)==get(n))
        {
            makeroot(1);
            access(n);
            splay(n,0);
            ans=min(ans,b[i].a+tval[tmax[n]]);
        }
    }
    if (ans==N) ans=-1;
    printf("%d\n",ans);
}