题意就不说了
感觉做了上一题就不觉得这题难啦2333(⊙ ▽ ⊙)
据说kruskal能拿70 黄学长那里有代码 戳这
当然,我们先来说说正解:LCT
根据经验其实就是看别人题解我们可以先限制a,b其中一个量,对a进行排序之后 我们就可以对b来用lct维护,原理跟上一题差不多,可以说是维护最小生成树(你喜欢怎么叫都行),反正就是每次把边加进去之前询问,是否已经联通(也就是要产生环),如果是,我们就要b比较小的那条 min(x y路径中的最大b值与当前边的b值)
最后每次询问一下1 和 N是否联通然后更新一下答案就好啦。。。
代码:(看完代码还有呢。。)
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define inf 1000000000
#define N 180010
using namespace std;
typedef long long LL;
struct edge{int x,y,a,b;}e[100010];
int fa[N],n,m,p,c[N][2],st[N],w[N],mx[N];
bool rev[N];
int Cmp(edge x1,edge x2){return x1.a<x2.a;}
bool Rt(int x)
{
if(c[fa[x]][0]==x || c[fa[x]][1]==x)return 0;
return 1;
}
void pushup(int x)
{
int l=c[x][0],r=c[x][1];
mx[x]=x;
if(w[mx[l]]>w[mx[x]])mx[x]=mx[l];
if(w[mx[r]]>w[mx[x]])mx[x]=mx[r];
}
void pushdown(int x)
{
int l=c[x][0],r=c[x][1];
if(rev[x])
{
rev[x]=0,rev[l]^=1,rev[r]^=1;
swap(c[x][0],c[x][1]);
}
}
void rotate(int x)
{
int y=fa[x],z=fa[y],a=c[y][1]==x,b=c[z][1]==y,g=c[x][!a];
if(!Rt(y))c[z][b]=x;
fa[g]=y,c[y][a]=g;
fa[y]=x,c[x][!a]=y;
fa[x]=z;
pushup(y); pushup(x);
}
void splay(int x)
{
int top=0,i;
for(i=x;!Rt(i);i=fa[i])st[++top]=i;
st[++top]=i;
for(i=top;i;i--)pushdown(st[i]);
while(!Rt(x))
{
int y=fa[x],z=fa[y],a=c[y][1]==x,b=c[z][1]==y;
if(!Rt(y))
{
if(a==b)rotate(y);
else rotate(x);
}
rotate(x);
}
}
void access(int x)
{
int last=0;
while(x)
{
splay(x);
c[x][1]=last;
pushup(x);
last=x,x=fa[x];
}
}
void make_root(int x)
{
access(x); splay(x);
rev[x]^=1;
}
void split(int x,int y)
{
make_root(x);
access(y); splay(y);
}
void link(int x,int y)
{
make_root(x); fa[x]=y;
}
void cut(int x,int y)
{
split(x,y);
fa[x]=c[y][0]=0;
}
int findrt(int x)
{
access(x); splay(x);
while(c[x][0])x=c[x][0];
return x;
}
inline int read()
{
int x=0; char ch=getchar();
while(ch<'0' || ch>'9')ch=getchar();
while(ch>='0' && ch<='9'){x=x*10+ch-'0'; ch=getchar();}
return x;
}
int main()
{
int i,x,y,s;
n=read(),m=read();
for(i=1;i<=m;i++)
e[i].x=read(),e[i].y=read(),e[i].a=read(),e[i].b=read();
sort(e+1,e+1+m,Cmp);
int ans=inf;
for(i=1;i<=m;i++)
{
x=e[i].x,y=e[i].y;
w[i+n]=mx[i+n]=e[i].b;
if(findrt(x)==findrt(y))
{
split(x,y);
s=mx[y];
if(w[s]>e[i].b)
{
cut(e[s-n].x,s),cut(e[s-n].y,s);
link(x,i+n),link(y,i+n);
}
}
else
link(x,i+n),link(y,i+n);
if(findrt(1)==findrt(n))
{
split(1,n); ans=min(ans,e[i].a+w[mx[n]]);
}
}
if(ans!=inf)printf("%d\n",ans);
else printf("-1\n");
return 0;
}
现在我要告诉你,这道题spfa也能过!
道理一样,排序了a之后我们动态加边(看起来高级而已)
用spfa维护路径上的最大b。 对于d数组不需要清空,每次多一个边就把两个端点放进去跑就好了。
当然了 还有优化我就把A一样的放一起跑罢了 (堆优化不会弄) 其他什么的可以看看这里点击打开链接
实际上不需要m次spfa,虽然不会算时间复杂度可是真的挺快。。(我只加了一个剪枝啊。。。)
下面就是我54行而且时间比lct快的spfa。。。
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define inf 1000000000
using namespace std;
struct edge{int x,y,a,b,next;}a[200100],b[100100];
int Cmp(edge x1,edge x2){return x1.a<x2.a;}
int len,first[50005];
void ins(int x,int y,int bb)
{
a[++len].x=x,a[len].y=y,a[len].b=bb;
a[len].next=first[x],first[x]=len;
a[++len].x=y,a[len].y=x,a[len].b=bb;
a[len].next=first[y],first[y]=len;
}
queue<int>q; int d[50005];
bool v[50005];
int main()
{
int n,m,i,x,y;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++)scanf("%d%d%d%d",&b[i].x,&b[i].y,&b[i].a,&b[i].b);
sort(b+1,b+1+m,Cmp);
for(i=2;i<=n;i++) d[i]=inf;
int ans=inf;
for(i=1;i<=m;i++)
{
ins(b[i].x,b[i].y,b[i].b);
q.push(b[i].x); q.push(b[i].y);
v[b[i].x]=v[b[i].y]=1;
if(b[i].a==b[i+1].a)continue;
while(!q.empty())
{
x=q.front();
for(int k=first[x];k;k=a[k].next)
{
y=a[k].y;
if(d[y]>max(d[x],a[k].b))
{
d[y]=max(d[x],a[k].b);
if(!v[y])q.push(y),v[y]=1;
}
}
q.pop();
v[x]=0;
}
if(ans>b[i].a+d[n])ans=b[i].a+d[n];
}
if(ans==inf)printf("-1\n");
else printf("%d\n",ans);
return 0;
}