JZOJ 3754 【NOI2014】魔法森林

来源:互联网 发布:ubuntu 阿里源 gpg 编辑:程序博客网 时间:2024/06/06 18:20

Description:

为了得到书法大家的真传,小 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 型守护精灵的个数之和。

Input:

输入文件的第 1 行包含两个整数 n, m,表示无向图共有 n 个节点, m 条边。

接下来 m 行,第 i + 1 行包含 4 个正整数 Xi, Yi, ai, bi, 描述第 i 条无向边。其中Xi与Yi为该边两个端点的标号,ai与bi的含义如题所述。

注意数据中可能包含重边与自环。

Output:

输出一行一个整数:如果小 E 可以成功拜访到隐士,输出小 E 最少需要携带的守护精灵的总个数;如果无论如何小 E 都无法拜访到隐士,输出“-1” (不含引号)。

Sample Input:

【样例输入 1】
4 5
1 2 19 1
2 3 8 12
2 4 12 15
1 3 17 8
3 4 1 17

【样例输入 2】
3 1
1 2 1 1

Sample Output:

【样例输出 1】
32

【样例输出 2】
-1

Data Constraint:

这里写图片描述

Hint:

【样例说明 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例题。
双关键字最小生成树。
把a排个序,依次添加边。
如果这条边的两端已在一棵树了,那么判断它们路径上的最大的b值是否大于当前要加的边,如果是,删掉那条边,加上这条边。
一直纠结于证明。
由于lct只能维护点权,所以把每条边都看作一个点。

#include<set>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#define fo(i, x, y) for(int i = x; i <= y; i ++)#define min(a, b) ((a) < (b) ? (a) : (b))using namespace std;const int Maxn = 50005, INF = 1e9;int n, m, tt, ans, f[Maxn * 3];int dd[Maxn * 3], fa[Maxn * 3], pf[Maxn * 3], t[Maxn * 3][2];struct edge {    int a, b, u, v;}e[Maxn * 2];struct node {    int rev, b, z;}a[Maxn * 3];bool rank_e(edge a, edge b) {    return a.a < b.a;}int find(int x) {return f[x] == x ? x : (f[x] = find(f[x]));}int lr(int x) {return t[fa[x]][1] == x;}void update(int x) {    if(x) {        a[x].b = x;        if(e[a[a[t[x][0]].b].z].b > e[a[a[x].b].z].b) a[x].b = a[t[x][0]].b;        if(e[a[a[t[x][1]].b].z].b > e[a[a[x].b].z].b) a[x].b = a[t[x][1]].b;    }}void down(int x) {    if(x && a[x].rev) {        swap(t[x][0], t[x][1]);        a[t[x][0]].rev ^= 1; a[t[x][1]].rev ^= 1;        a[x].rev = 0;    }}void rotate(int x) {    int y = fa[x], k = lr(x);    t[y][k] = t[x][!k]; if(t[x][!k]) fa[t[x][!k]] = y;    fa[x] = fa[y]; if(fa[y]) t[fa[y]][lr(y)] = x;    t[x][!k] = y; fa[y] = x; pf[x] = pf[y];    update(y); update(x);}void xc(int x) {    for(; x; x = fa[x]) dd[++ dd[0]] = x;    for(; dd[0]; dd[0] --) down(dd[dd[0]]);}void splay(int x, int y) {    xc(x);    while(fa[x] != y) {        if(fa[fa[x]] != y)            if(lr(x) == lr(fa[x])) rotate(fa[x]); else rotate(x);        rotate(x);    }}void access(int x) {    for(int y = 0; x; y = x, x = pf[x])        splay(x, 0), fa[t[x][1]] = 0, pf[t[x][1]] = x,        t[x][1] = y, fa[y] = x, pf[y] = 0, update(x);}void makeroot(int x) {    access(x); splay(x, 0); a[x].rev ^= 1;}void link(int x, int y, int z, int b) {    a[z].z = b; a[z].b = b;    makeroot(x); pf[x] = z;    makeroot(z); pf[z] = y;}void cut(int x, int y) {    makeroot(x);    access(y); splay(y, 0);    fa[x] = pf[x] = t[y][0] = 0;    update(y);}void Init() {    scanf("%d %d", &n, &m);    fo(i, 1, m) scanf("%d %d %d %d", &e[i].u, &e[i].v, &e[i].a, &e[i].b);    sort(e + 1, e + m + 1, rank_e);}void dg(int x) {    if(x == 0) return;    printf("%d %d %d\n", x, t[x][0], t[x][1]);    dg(t[x][0]); dg(t[x][1]);}int bz[Maxn];void End() {    tt = n;     e[0].b = -INF;    ans = INF;    fo(i, 1, n) update(i);    fo(i, 1, n) f[i] = i;    fo(i, 1, m) {        int x = e[i].u, y = e[i].v;        if(x == y) continue;        if(find(x) != find(y)) {            f[f[x]] = f[y];            tt ++;            link(x, y, tt, i);            makeroot(x); splay(x, 0);        } else {            makeroot(x);             access(y); splay(x, 0);            int g = a[x].b;            if(e[a[g].z].b > e[i].b) {                cut(e[a[g].z].u, e[a[g].z].v);                tt ++;                link(e[i].u, e[i].v, tt, i);            }        }        if(find(1) == find(n)) {            makeroot(n); access(1); splay(n, 0);            ans = min(ans, e[i].a + e[a[a[n].b].z].b);        }    }    if(ans == INF) printf("-1"); else printf("%d", ans);}int main() {    Init();    End();}