luogu P2387 [NOI2014] 魔法森林
题面传送门
不判重边调一年。
首先这道题看上去很不好做。考虑枚举一个然后算另一个。
那么就是要找所有第一个边权小于当前值的边构成的图中\(1\)与\(n\)之间的最小瓶颈路。这个东西用最小生成树+倍增就可以解决。
但是这棵最小生成树在动,所以直接用LCT维护即可。
注意一定要判重边。
code:
#include<cstdio>
#include<algorithm>
#include<map>
#define I inline
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int n,m,k,x,y,z,ans=2e9,tot,cnt,last;
map<int,int> fs[50039];
struct pai{
int a,x,y;
pai operator +(const pai &s)const{return s.a>a?s:(pai){a,x,y};}
}now;
inline void swap(int &x,int &y){x^=y^=x^=y;}
struct linkcuttree{
int fa[200039],flag[200039],l[200039],r[200039],st[200039],sh;pai f[200039],sum[200039];
I void up(int x){sum[x]=sum[l[x]]+sum[r[x]]+f[x];}
I void push(int x){x&&(swap(l[x],r[x]),flag[x]^=1);}
I void pushdown(int x){flag[x]&&(push(l[x]),push(r[x]),flag[x]=0);}
I int child(int x){return l[fa[x]]==x||r[fa[x]]==x;}
I int wrt(int x){return r[fa[x]]==x;}
I void rotate(int x){
int y=fa[x],z=fa[y],b=(x==l[y])?r[x]:l[x];
child(y)&&((y==l[z]?l[z]:r[z])=x);(x==l[y])?(r[x]=y,l[y]=b):(l[x]=y,r[y]=b);
fa[y]=x,fa[x]=z,b&&(fa[b]=y);up(y);up(x);
}
I void splay(int x){
int y=x;st[sh=1]=x;while(child(y)) st[++sh]=y=fa[y];
while(sh) pushdown(st[sh--]);while(child(x)) child(fa[x])&&(rotate(wrt(x)^wrt(fa[x])?x:fa[x]),0),rotate(x);
}
I void access(int x){for(int y=0;x;x=fa[y=x]) splay(x),r[x]=y,up(x);}
I void makeroot(int x){access(x);splay(x);push(x);}
I int findroot(int x){access(x);splay(x);while(l[x])pushdown(x),x=l[x];return splay(x),x;}
I void link(int x,int y){/*if(findroot(x)==findroot(y))printf("-1\n");*/makeroot(x);makeroot(y);fa[x]=y;}
I void cut(int x,int y){split(x,y);r[y]=fa[x]=0;up(y);}
I void split(int x,int y){makeroot(x);access(y);splay(y);}
}s;
struct ques{int x,y,a,b;}f[100039],tmp;
I bool cmp(ques x,ques y){return x.a<y.a;}
I void cut(int x,int y){s.cut(x,fs[x][y]);s.cut(y,fs[x][y]);fs[x][y]=fs[y][x]=0;}
I void link(int x,int y,pai f){/*if(s.findroot(x)==s.findroot(y))printf("-1\n");*/s.f[cnt]=s.sum[cnt]=f;s.link(x,cnt);s.link(y,cnt);fs[x][y]=fs[y][x]=cnt;}
int main(){
freopen("1.in","r",stdin);
register int i;
scanf("%d%d",&n,&m);cnt=n;
for(i=1;i<=m;i++) scanf("%d%d%d%d",&f[i].x,&f[i].y,&f[i].a,&f[i].b);sort(f+1,f+m+1,cmp);
for(i=1;i<=m;i++){
tmp=f[i];last=fs[tmp.x][tmp.y];++cnt;
if(s.findroot(tmp.x)^s.findroot(tmp.y))link(tmp.x,tmp.y,(pai){tmp.b,tmp.x,tmp.y});
else if(!last)s.split(tmp.x,tmp.y),now=s.sum[tmp.y],now.a>tmp.b&&(cut(now.x,now.y),link(tmp.x,tmp.y,(pai){tmp.b,tmp.x,tmp.y}),0);
else if(tmp.b<f[last-n].b) cut(tmp.x,tmp.y),link(tmp.x,tmp.y,(pai){tmp.b,tmp.x,tmp.y});
if(s.findroot(1)==s.findroot(n))s.split(1,n),ans=min(ans,s.sum[n].a+tmp.a);
}
printf("%d\n",ans==2e9?-1:ans);
}