LCT打卡了,啦啦啦
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int MAXN=55555;
const int MAXM=111111;
struct edge_t
{
int u,v,w1,w2;
bool operator < (const edge_t& r)const
{
return w1<r.w1;
}
}edge[MAXM];
//现在有点逗比的就是这个现在我们要把每个边权对应到一个点权
//那我们怎么做呢,首先的想法是把一条边拆成一个点和两条边~~~
const int SIZE=MAXN+MAXM;
int ch[SIZE][2],pre[SIZE],key[SIZE];
int rev[SIZE],Max[SIZE];
bool rt[SIZE];
void Update_Rev(int r)
{
if(!r) return;
swap(ch[r][0],ch[r][1]);
rev[r]^=1;
}
void push_down(int r)
{
if(rev[r])
{
Update_Rev(ch[r][0]);
Update_Rev(ch[r][1]);
rev[r]=0;
}
}
void push_up(int r)
{
Max[r]=max(key[r],max(Max[ch[r][0]],Max[ch[r][1]]));
}
void Rotate(int x)
{
int y=pre[x],kind=ch[y][1]==x;
ch[y][kind]=ch[x][!kind];
pre[ch[y][kind]]=y;
pre[x]=pre[y];
pre[y]=x;
ch[x][!kind]=y;
if(rt[y]) rt[y]=false,rt[x]=true;
else ch[pre[x]][ch[pre[x]][1]==y]=x;
push_up(y);
}
void P(int r)
{
if(!rt[r]) P(pre[r]);
push_down(r);
}
void Splay(int r)
{
P(r);
while(!rt[r])
{
int f=pre[r],ff=pre[f];
if(rt[f]) Rotate(r);
else if((ch[ff][1]==f)==(ch[f][1]==r)) Rotate(f),Rotate(r);
else Rotate(r),Rotate(r);
}
push_up(r);
}
int Access(int x)
{
int y=0;
for(;x;x=pre[y=x])
{
Splay(x);
rt[ch[x][1]]=true,rt[ch[x][1]=y]=false;
push_up(x);
}
return y;
}
bool Judge(int u,int v)
{
while(pre[u]) u=pre[u];
while(pre[v]) v=pre[v];
return u==v;
}
void mroot(int r)
{
Access(r);
Splay(r);
Update_Rev(r);
}
void link(int u,int v)
{
mroot(u);
pre[u]=v;
}
void cut(int u,int v)
{
mroot(u);
Splay(v);
pre[ch[v][0]]=pre[v];
pre[v]=0;
rt[ch[v][0]]=true;
ch[v][0]=0;
push_up(v);
}
void lca(int &u,int &v)
{
Access(v),v=0;
while(u)
{
Splay(u);
if(!pre[u]) return;
rt[ch[u][1]]=true;
rt[ch[u][1]=v]=false;
push_up(u);
u=pre[v=u];
}
}
int query(int u,int v)
{
if(!Judge(u,v)) return -1;
lca(u,v);
return max(key[u],max(Max[v],Max[ch[u][1]]));
}
//找到这一棵伸展树上最大的节点的编号
int find(int r)
{
int Maxs=Max[r];
if(key[r]==Maxs) return r;
if(Max[ch[r][0]]==Maxs) return find(ch[r][0]);
else return find(ch[r][1]);
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=1;i<=m;i++)
scanf("%d%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w1,&edge[i].w2);
sort(edge+1,edge+m+1);
for(int i=1;i<=n+m;i++)
{
if(i>n) key[i]=Max[i]=edge[i-n].w2;
else key[i]=Max[i]=0;
pre[i]=ch[i][0]=ch[i][1]=0;
rev[i]=0;
rt[i]=true;
}
//然后我这里用LCT维护最小生成树
//link-cut tree查询这一段路径上面的最小值
int ans=-1;
for(int i=1;i<=m;i++)
{
int u=edge[i].u;
int v=edge[i].v;
int w=edge[i].w2;
int Edge=i+n;
//首先我们判断一下u,v的连通性,如果u,v两个节点是联通的,那么我们把这条边加上去明显会形成环对吧
//如果当前加入的节点距离比这条路径上面的最大值的那一条边小的话
//那么我们用这条边代替那条边就行
int Maxs=query(u,v);
if(Maxs==-1)
{
link(u,Edge);
link(Edge,v);
}
else if(Maxs>w)
{
//我们现在要找到一条最大的边,并且取缔这条边
int rt;
int tmpu=u,tmpv=v;
lca(tmpu,tmpv);
if(Maxs==Max[tmpv]) rt=find(tmpv);
else rt=find(ch[tmpu][1]);
int cutu=edge[rt-n].u;
int cutv=edge[rt-n].v;
cut(cutu,rt);
cut(rt,cutv);
link(u,Edge);
link(Edge,v);
}
int tmpans=query(1,n);
if(tmpans!=-1)
{
if(ans==-1) ans=tmpans+edge[i].w1;
else ans=min(ans,tmpans+edge[i].w1);
}
}
printf("%d\n",ans);
}
return 0;
}