题目描述
为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐士。魔法森林可以被看成一个包含 nn 个节点 mm 条边的无向图,节点标号为 1,2,3,…,n1,2,3,…,n,边标号为 1,2,3,…,m1,2,3,…,m。初始时小 E 同学在 11 号节点,隐士则住在 nn 号节点。小 E 需要通过这一片魔法森林,才能够拜访到隐士。
魔法森林中居住了一些妖怪。每当有人经过一条边的时候,这条边上的妖怪 就会对其发起攻击。幸运的是,在 11 号节点住着两种守护精灵:A 型守护精灵与 B 型守护精灵。小 E 可以借助它们的力量,达到自己的目的。
只要小 E 带上足够多的守护精灵,妖怪们就不会发起攻击了。具体来说,无向图中的每一条边 e_iei 包含两个权值 a_iai 与 b_ibi 。若身上携带的 A 型守护精灵个数不少于 a_iai ,且 B 型守护精灵个数不少于 b_ibi ,这条边上的妖怪就不会对通过这条边的人发起攻击。当且仅当通过这片魔法森林的过程中没有任意一条边的妖怪向 小 E 发起攻击,他才能成功找到隐士。
由于携带守护精灵是一件非常麻烦的事,小 E 想要知道,要能够成功拜访到 隐士,最少需要携带守护精灵的总个数。守护精灵的总个数为 A 型守护精灵的个数与 B 型守护精灵的个数之和。
输入格式
输入文件的第 11 行包含两个整数 n,mn,m,表示无向图共有 nn 个节点,mm 条边。 接下来 mm 行,第 i+1i+1 行包含 44 个正整数 X_i,Y_i,a_i,b_iXi,Yi,ai,bi,描述第 ii 条无向边。 其中 X_iXi 与 Y_iYi 为该边两个端点的标号,a_iai 与 b_ibi 的含义如题所述。 注意数据中可能包含重边与自环。
输出格式
输出一行一个整数:如果小 E 可以成功拜访到隐士,输出小 E 最少需要携 带的守护精灵的总个数;如果无论如何小 E 都无法拜访到隐士,输出
-1
。输入输出样例
输入 #1复制
4 5 1 2 19 1 2 3 8 12 2 4 12 15 1 3 17 8 3 4 1 17输出 #1复制
32输入 #2复制
3 1 1 2 1 1输出 #2复制
-1说明/提示
* 解释1
如果小 E 走路径 1\to 2\to 41→2→4,需要携带 19+15=3419+15=34 个守护精灵; 如果小 E 走路径 1\to 3\to 41→3→4,需要携带 17+17=3417+17=34 个守护精灵; 如果小 E 走路径 1\to 2\to 3\to 41→2→3→4,需要携带 19+17=3619+17=36 个守护精灵; 如果小 E 走路径 1\to 3\to 2\to 41→3→2→4,需要携带 17+15=3217+15=32 个守护精灵。 综上所述,小 E 最少需要携带 3232 个守护精灵。
* 解释2
小 E 无法从 11 号节点到达 33 号节点,故输出
-1
。题解:
每条边有两个属性ai和bi 我们把边按照ai排序 lct维护bi的最大值 然后依次往lct维护的森林中加入 如果当一条边加入的时候形成了环(假如加入边e(u,v)形成了环) 那么我们要找链路(u,v)上bi值最大的边 把它与当前边的bi值比较 如果它的值大于当前边的bi 我们把这个bi值最大的边去掉 把当前边连上 通过lct判断 1和n的连通性 如果连通就取链(1,n)上bi的最大值与当前边的ai的和更新答案(当前边的ai可能不在链(1,n)上 但是它不会覆盖答案的最小值)
#include<bits/stdc++.h>
#define lc c[x][0]
#define rc c[x][1]
#define I inline void
#define R register int
using namespace std;
const int N = 3e5+10;
int f[N],c[N][2],r[N],val[N],mx[N],st[N];
inline int in(){
int w=0,x=0;char c=0;
while(c>'9'||c<'0') w|=c=='-',c=getchar();
while(c<='9'&&c>='0') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return w?-x:x;
}
inline bool nroot(R x){
return c[f[x]][0]==x||c[f[x]][1]==x;
}
I pushup(R x){
mx[x]=x;
if(val[mx[x]]<val[mx[lc]]) mx[x]=mx[lc];
if(val[mx[x]]<val[mx[rc]]) mx[x]=mx[rc];
}
I pushr(R x){
swap(lc,rc);
r[x]^=1;
}
I pushdown(R x){
if(r[x]){
if(lc) pushr(lc);
if(rc) pushr(rc);
r[x]=0;
}
}
I rotate(R x){
R y=f[x],z=f[y],k=c[y][1]==x,w=c[x][k^1];
if(nroot(y)) c[z][c[z][1]==y]=x; c[x][k^1]=y;c[y][k]=w;
if(w) f[w]=y; f[x]=z;f[y]=x;
pushup(y);
}
I splay(R x){
R y=x,z=0;
st[++z]=y;
while(nroot(y)) st[++z]=y=f[y];
while(z) pushdown(st[z--]);
while(nroot(x)){
y=f[x],z=f[y];
if(nroot(y)) rotate((c[y][1]==x)^(c[z][1]==y)?x:y);
rotate(x);
}
pushup(x);
}
I access(R x){
for(R y=0;x;x=f[y=x])
splay(x),rc=y,pushup(x);
}
I makeroot(R x){
access(x);splay(x);
pushr(x);
}
I split(R x,R y){
makeroot(x);
access(y);splay(y);
}
inline int findroot(R x){
access(x);splay(x);
while(lc) pushdown(x),x=lc;
splay(x);
return x;
}
inline bool judge(R x,R y){
makeroot(x);
return findroot(y)==x;
}
I link(R x,R y){
makeroot(x);
if(findroot(y)!=x) f[x]=y;
}
I cut(R x,R y){
makeroot(x);
if(findroot(y)==x&&f[y]==x&&c[y][0]==0){
f[y]=c[x][1]=0;
pushup(x);
}
}
struct edge{
int x,y,a,b;
bool operator < (const edge& t)const{
return a<t.a;
}
}e[N];
int main(){
int n,m;
n=in();m=in();
for(int i = 1; i <= m; i++){
e[i].x=in();e[i].y=in();
e[i].a=in();e[i].b=in();
}
sort(e+1,e+1+m);
int ans = 2e9;
for(int i = 1; i <= m; i++){
val[i+n]=e[i].b;
mx[i+n]=i+n;
int u=e[i].x,v=e[i].y;
if(judge(u,v)){
split(u,v);
int now=mx[v];
if(val[now]>e[i].b){
cut(now,e[now-n].x);cut(now,e[now-n].y);
link(i+n,u);link(i+n,v);
}
}else{
link(u,i+n);link(v,i+n);
}
if(judge(1,n)){
split(1,n);
ans=min(ans,val[mx[n]]+e[i].a);
}
}
if(ans==2e9) printf("-1\n");
else printf("%d\n",ans);
return 0;
}