题目链接
https://www.lydsy.com/JudgeOnline/problem.php?id=3669
题解
将一个a-b,权值为c的边看成a-e-b,其中a,b权值为0,e的权值为c,这样就把边权变成了点权。
首先将所有边按照排序,用LCT维护点和边的连通情况,边权为。
考虑新加入一条从到,权值为的边。显然,如果这条边对到的答案有影响,那么答案就可以对取。如果这条边对答案没有影响,那么答案必定已经前述式子,对前述式子更新不会对答案有任何影响(不是最小的,号后面值的是相同的)。
- 如果与不连通,那么加入这条边不会对答案产生不利的影响。
- 如果与连通,这个时候分两种情况考虑。
- 若到的最大值,加入这条边必定不会使到的边权最大值变小,因此选择不加入。
- 若到的最大值,此时删去到中边权最大的一个,必定不会使到的边权最大值变大,因此可以这样操作。
可以证明,通过上述操作,得出的解一定是最优的。
代码
#include <cstdio>
#include <algorithm>
int read()
{
int x=0,f=1;
char ch=getchar();
while((ch<'0')||(ch>'9'))
{
if(ch=='-')
{
f=-f;
}
ch=getchar();
}
while((ch>='0')&&(ch<='9'))
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
const int maxn=50000;
const int maxm=100000;
const int maxk=maxn+maxm;
const int inf=0x3f3f3f3f;
struct node
{
node *son[2],*fa,*pos;
int self,val,rev;
};
node tree[maxk+10];
namespace lct
{
int isroot(node *x)
{
if(x->fa==NULL)
{
return 1;
}
return !((x->fa->son[0]==x)||(x->fa->son[1]==x));
}
int t(node *x)
{
return x->fa->son[1]==x;
}
int pushdown(node *x)
{
if(x->rev)
{
x->rev=0;
std::swap(x->son[0],x->son[1]);
if(x->son[0]!=NULL)
{
x->son[0]->rev^=1;
}
if(x->son[1]!=NULL)
{
x->son[1]->rev^=1;
}
}
return 0;
}
int updata(node *x)
{
int l=(x->son[0]==NULL)?0:x->son[0]->val,r=(x->son[1]==NULL)?0:x->son[1]->val;
x->val=std::max(x->self,std::max(l,r));
x->pos=(x->self>=std::max(l,r))?x:((l>r)?x->son[0]->pos:x->son[1]->pos);
return 0;
}
int rotate(node *x)
{
node *f=x->fa;
pushdown(f);
pushdown(x);
int k=t(x);
if(!isroot(f))
{
f->fa->son[t(f)]=x;
}
x->fa=f->fa;
f->fa=x;
if(x->son[k^1]!=NULL)
{
x->son[k^1]->fa=f;
}
f->son[k]=x->son[k^1];
x->son[k^1]=f;
updata(x);
updata(f);
return 0;
}
int splay_root(node *x)
{
while(!isroot(x))
{
node *f=x->fa;
if(isroot(f))
{
rotate(x);
}
else if(t(f)==t(x))
{
rotate(f);
rotate(x);
}
else
{
rotate(x);
rotate(x);
}
}
pushdown(x);
return 0;
}
int access(node *x)
{
node *y=NULL;
while(x!=NULL)
{
splay_root(x);
x->son[1]=y;
if(y!=NULL)
{
y->fa=x;
}
updata(x);
y=x;
x=y->fa;
}
return 0;
}
int makeroot(node *x)
{
access(x);
splay_root(x);
x->rev^=1;
return 0;
}
int link(node *x,node *y)
{
makeroot(x);
x->fa=y;
return 0;
}
int cut(node *x,node *y)
{
makeroot(x);
access(y);
splay_root(y);
y->son[0]=x->fa=NULL;
updata(y);
return 0;
}
node* find(node *x)
{
access(x);
splay_root(x);
while(x->son[0]!=NULL)
{
x=x->son[0];
pushdown(x);
}
return x;
}
int check(node *x,node *y)
{
return find(x)==find(y);
}
node* getmax(node *x)
{
splay_root(x);
int v=x->val;
while(x!=NULL)
{
if(x->self==v)
{
return x;
}
if((x->son[0]!=NULL)&&(x->son[0]->val==v))
{
x=x->son[0];
}
else
{
x=x->son[1];
}
pushdown(x);
}
return 0;
}
node* getpn(node *x,int op)
{
splay_root(x);
x=x->son[op];
pushdown(x);
while(x->son[op^1]!=NULL)
{
x=x->son[op^1];
pushdown(x);
}
return x;
}
int check_link(node *x,node *y,node *e)
{
if(check(x,y))
{
makeroot(x);
access(y);
splay_root(y);
if(y->val>e->self)
{
node *d=y->pos;
node *p=getpn(d,0),*q=getpn(d,1);
cut(d,p);
cut(d,q);
}
else
{
return 0;
}
}
link(e,x);
link(e,y);
return 0;
}
int getmax(node *x,node *y)
{
makeroot(x);
access(y);
splay_root(y);
return y->val;
}
}
struct edge
{
int x,y,a,b;
bool operator <(const edge &other) const
{
if(a==other.a)
{
return b<other.b;
}
return a<other.a;
}
};
edge e[maxm+10];
int n,m;
int main()
{
n=read();
m=read();
for(int i=1; i<=m; ++i)
{
e[i].x=read();
e[i].y=read();
e[i].a=read();
e[i].b=read();
}
std::sort(e+1,e+m+1);
for(int i=1; i<=n; ++i)
{
tree[i].son[0]=tree[i].son[1]=tree[i].fa=NULL;
tree[i].self=tree[i].val=tree[i].rev=0;
}
int ans=inf;
node *start=&tree[1],*end=&tree[n];
for(int i=1; i<=m; ++i)
{
tree[i+n].son[0]=tree[i+n].son[1]=tree[i+n].fa=NULL;
tree[i+n].self=tree[i+n].val=e[i].b;
tree[i+n].rev=0;
node *l=&tree[e[i].x],*r=&tree[e[i].y],*ed=&tree[i+n];
lct::check_link(l,r,ed);
if(lct::check(start,end))
{
ans=std::min(ans,e[i].a+lct::getmax(start,end));
}
}
printf("%d\n",(ans==inf)?-1:ans);
return 0;
}