题目描述
为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐 士。魔法森林可以被看成一个包含 n 个节点 m 条边的无向图,节点标号为 1,2,3,…,n,边标号为 1,2,3,…,m。初始时小 E 同学在 1 号节点,隐士则住在 n 号节点。小 E 需要通过这一片魔法森林,才能够拜访到隐士。
魔法森林中居住了一些妖怪。每当有人经过一条边的时候,这条边上的妖怪 就会对其发起攻击。幸运的是,在 1 号节点住着两种守护精灵:A 型守护精灵与 B 型守护精灵。小 E 可以借助它们的力量,达到自己的目的。
只要小 E 带上足够多的守护精灵,妖怪们就不会发起攻击了。具体来说,无 向图中的每一条边 ei 包含两个权值 ai 与 bi 。若身上携带的 A 型守护精灵个数不 少于 ai ,且 B 型守护精灵个数不少于 bi ,这条边上的妖怪就不会对通过这条边 的人发起攻击。当且仅当通过这片魔法森林的过程中没有任意一条边的妖怪向 小 E 发起攻击,他才能成功找到隐士。
由于携带守护精灵是一件非常麻烦的事,小 E 想要知道,要能够成功拜访到 隐士,最少需要携带守护精灵的总个数。守护精灵的总个数为 A 型守护精灵的 个数与 B 型守护精灵的个数之和。
输入输出格式
输入格式:
输入文件的第 1 行包含两个整数 n,m,表示无向图共有 n 个节点,m 条边。 接下来 m 行,第i+ 1 行包含 4 个正整数 Xi,Yi,ai,bi,描述第i条无向边。 其中Xi与 Yi为该边两个端点的标号,ai 与 bi 的含义如题所述。 注意数据中可能包含重边与自环。
输出格式:
输出一行一个整数:如果小 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→2→4,需要携带 19+15=34 个守护精灵; 如果小 E 走路径 1→3→4,需要携带 17+17=34 个守护精灵; 如果小 E 走路径 1→2→3→4,需要携带 19+17=36 个守护精灵; 如果小 E 走路径 1→3→2→4,需要携带 17+15=32 个守护精灵。 综上所述,小 E 最少需要携带 32 个守护精灵。
解释2
小 E 无法从 1 号节点到达 3 号节点,故输出-1。
分析:显然是一道LCT维护最小生成树的水题,数组范围需要注意。
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
const int maxn=5e4+5;
const int maxe=1e5+5;
using namespace std;
struct tree{
int fa,l,r,data,id;
bool rev;
}t[maxn+maxe];
struct node{
int x,y,a,b;
}e[maxe];
int n,m,i,ans;
int val[maxn+maxe];
bool cmp(node x,node y)
{
return x.a<y.a;
}
void updata(int x)
{
int l=t[x].l,r=t[x].r;
if (t[l].data>t[r].data) t[x].data=t[l].data,t[x].id=t[l].id;
else t[x].data=t[r].data,t[x].id=t[r].id;
if (val[x]>t[x].data) t[x].data=val[x],t[x].id=x;
}
bool isroot(int x)
{
return (x!=t[t[x].fa].l) && (x!=t[t[x].fa].r);
}
void remove(int x)
{
if (!isroot(x)) remove(t[x].fa);
if (t[x].rev)
{
t[x].rev^=1;
swap(t[x].l,t[x].r);
if (t[x].l) t[t[x].l].rev^=1;
if (t[x].r) t[t[x].r].rev^=1;
}
}
void rttr(int x)
{
int y=t[x].l;
t[x].l=t[y].r;
if (t[y].r) t[t[y].r].fa=x;
if (x==t[t[x].fa].l) t[t[x].fa].l=y;
else if (x==t[t[x].fa].r) t[t[x].fa].r=y;
t[y].fa=t[x].fa;
t[x].fa=y;
t[y].r=x;
updata(x); updata(y);
}
void rttl(int x)
{
int y=t[x].r;
t[x].r=t[y].l;
if (t[y].l) t[t[y].l].fa=x;
if (x==t[t[x].fa].l) t[t[x].fa].l=y;
else if (x==t[t[x].fa].r) t[t[x].fa].r=y;
t[y].fa=t[x].fa;
t[x].fa=y;
t[y].l=x;
updata(x); updata(y);
}
void splay(int x)
{
remove(x);
while (!isroot(x))
{
int p=t[x].fa,g=t[p].fa;
if (isroot(p))
{
if (x==t[p].l) rttr(p);
else rttl(p);
}
else
{
if (x==t[p].l)
{
if (p==t[g].l) rttr(p),rttr(g);
else rttr(p),rttl(g);
}
else
{
if (p==t[g].l) rttl(p),rttr(g);
else rttl(p),rttl(g);
}
}
}
}
void access(int x)
{
int y=0;
while (x)
{
splay(x);
t[x].r=y;
updata(x);
y=x;x=t[x].fa;
}
}
void makeroot(int x)
{
access(x);
splay(x);
t[x].rev^=1;
}
int find(int x)
{
if (isroot(x)) return x;
else return find(t[x].fa);
}
void link(int x,int y)
{
makeroot(x);
access(y);
splay(y);
t[x].fa=y;
}
void cut(int x,int y)
{
makeroot(x);
access(y);
splay(y);
t[x].fa=0; t[y].l=0;
}
bool check(int x,int y)
{
makeroot(x);
access(y);
splay(y);
return find(x)==y;
}
int main()
{
scanf("%d%d",&n,&m);
for (i=1;i<=m;i++)
scanf("%d%d%d%d",&e[i].x,&e[i].y,&e[i].a,&e[i].b);
sort(e+1,e+m+1,cmp);
ans=0x3f3f3f3f;
for (i=1;i<=m;i++)
{
if (check(e[i].x,e[i].y) && (t[e[i].y].data>e[i].b))
{
int u=t[e[i].y].id;
cut(e[u-n].x,u);
cut(u,e[u-n].y);
}
val[i+n]=e[i].b;
link(e[i].x,n+i);
link(n+i,e[i].y);
if (check(1,n))
{
ans=min(ans,t[n].data+e[i].a);
}
}
if (ans==0x3f3f3f3f) printf("-1");
else printf("%d",ans);
}