为了得到书法大家的真传,小 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”(不 含引号)。
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
如果小 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 个守护精灵。
小 E 无法从 1 号节点到达 3 号节点,故输出-1。
听说此题 SPFA S P F A 可以过?蒟蒻表示不会这种骚操作…只好老老实实打LCT。
我们可以将所有边按A值排序, 然后一条一条像Kruskal一样连接, 并用并查集判联通。 如果已联通, 就找出原边上的最大B值的点进行比较,若比其小则将它替换掉更优。(因为已经保证A尽量小了)
值得注意的是, 这里LCT维护的是边权, 所以将边作为实点, 原来的节点作为虚节点, 与两端虚节点相连即可达到联通的目的。
代码如下:
#include
#include
#include
#include
#include
#include
#define R register
#define gc getchar()
#define IN inline
#define W while
#define MX 100005
#define ls tree[now].son[0]
#define rs tree[now].son[1]
#define dad tree[now].fat
template <class T>
IN void in (T &x)
{
x = 0; R char c = gc;
W (!isdigit(c)) c = gc;
W (isdigit(c))
{x = (x << 1) + (x << 3) + c - 48, c = gc;}
}
namespace LCT
{
struct Node
{
int son[2], fat, mx, pos, val;//pos维护子树中最大b值的位置
bool rev;
}tree[(MX << 1) + 10];
int st[MX], top, line, num, f[MX], dot;
struct Edge
{
int from, to, val_a, val_b;
}edge[MX];
IN bool operator < (const Edge &x, const Edge &y)
{return x.val_a < y.val_a;}
IN bool nroot(const int &now)
{return tree[dad].son[1] == now || tree[dad].son[0] == now;}
IN bool get(const int &now)
{return tree[dad].son[1] == now;}
IN void pushrev(const int &now)
{
std::swap(ls, rs);
tree[now].rev ^= 1;
}
IN void pushup(const int &now)
{
tree[now].mx = tree[now].val, tree[now].pos = now;
if(ls) if(tree[ls].mx > tree[now].mx) tree[now].mx = tree[ls].mx, tree[now].pos = tree[ls].pos;
if(rs) if(tree[rs].mx > tree[now].mx) tree[now].mx = tree[rs].mx, tree[now].pos = tree[rs].pos;
}
IN void pushdown(const int &now)
{
if(tree[now].rev)
{
if(ls) pushrev(ls);
if(rs) pushrev(rs);
}
tree[now].rev = false;
}
IN void rotate(const int &now)
{
R bool dir = get(now);
R int fa = dad, grand = tree[fa].fat;
tree[fa].son[dir] = tree[now].son[dir ^ 1];
tree[tree[now].son[dir ^ 1]].fat = fa;
if(nroot(fa)) tree[grand].son[get(fa)] = now;
tree[now].fat = grand;
tree[fa].fat = now;
tree[now].son[dir ^ 1] = fa;
pushup(fa);
}
IN void splay(int now)
{
R int fa, grand, x = now; top = 0;
st[++top] = x;
W (nroot(x)) x = tree[x].fat, st[++top] = x;
W (top) pushdown(st[top--]);
W (nroot(now))
{
fa = dad, grand = tree[fa].fat;
if(nroot(fa)) rotate(get(now) == get(fa) ? fa : now);
rotate(now);
}
pushup(now);
}
IN void access(int now)
{
for (R int x = 0; now; x = now, now = dad)
splay(now), tree[now].son[1] = x, pushup(now);
}
IN void make_root(int now)
{access(now), splay(now), pushrev(now);}
IN int find_root(int now)
{
access(now), splay(now);
W (ls) pushdown(now), now = ls;
return now;
}
IN void link(const int &x, const int &y)
{
make_root(x);
if(find_root(y) != x)
tree[x].fat = y;
}
IN void cut(const int &x, const int &y)
{
make_root(x);
if(find_root(y) == x && tree[x].fat == y && !tree[x].son[1])
tree[x].fat = tree[y].son[0] = 0, pushup(y);
}
int find(const int &x)
{return f[x] == x ? x : f[x] = find(f[x]);}
IN void split(const int &x, const int &y)
{make_root(x), access(y), splay(y);}
}
using namespace LCT;
int main(void)
{
int ans = 999999, v, t1, t2, f1, f2;
in(dot), in(line);
for (R int i = 1; i <= line; ++i)
in(edge[i].from), in(edge[i].to), in(edge[i].val_a), in(edge[i].val_b);
for (R int i = 1; i <= 100000; ++i) f[i] = i;
std::sort(edge + 1, edge + 1 + line);
for (R int i = 1; i <= line; ++i)
{
tree[i].val = edge[i].val_b;
t1 = find(edge[i].from), t2 = find(edge[i].to);
if(t1 != t2)
{
link(edge[i].from + MX, i);
link(edge[i].to + MX, i);//加上MX的点为原树中的点, 点权为0
//不加MX的点为实际的排好序的边
f[t1] = t2;
if(find(1) == find(dot))
{
split(1 + MX, dot + MX);
if(ans > tree[dot + MX].mx + edge[i].val_a)
ans = tree[dot + MX].mx + edge[i].val_a;
}
}
else
{
split(edge[i].to + MX, edge[i].from + MX);
if(tree[edge[i].from + MX].mx > edge[i].val_b)
{
v = tree[edge[i].from + MX].pos;
f1 = edge[v].from;
f2 = edge[v].to;
cut(f1 + MX, v);
cut(f2 + MX, v);
link(edge[i].to + MX, i);
link(edge[i].from + MX, i);
if(find(1) == find(dot))
{
split(1 + MX, dot + MX);
if(ans > edge[i].val_a + tree[dot + MX].mx)
ans = edge[i].val_a + tree[dot + MX].mx;
}
}
}
}
if(ans == 999999) printf("-1");
else printf("%d", ans);
}