您的位置:首页 > 其它

【bzoj3669】[Noi2014]魔法森林

2017-08-24 21:10 295 查看

3669: [Noi2014]魔法森林

Time Limit: 30 Sec  Memory Limit: 512 MB
Submit: 2884  Solved: 1806

[Submit][Status][Discuss]

Description

为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士。魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M。初始时小E同学在号节点1,隐士则住在号节点N。小E需要通过这一片魔法森林,才能够拜访到隐士。

魔法森林中居住了一些妖怪。每当有人经过一条边的时候,这条边上的妖怪就会对其发起攻击。幸运的是,在号节点住着两种守护精灵:A型守护精灵与B型守护精灵。小E可以借助它们的力量,达到自己的目的。

只要小E带上足够多的守护精灵,妖怪们就不会发起攻击了。具体来说,无向图中的每一条边Ei包含两个权值Ai与Bi。若身上携带的A型守护精灵个数不少于Ai,且B型守护精灵个数不少于Bi,这条边上的妖怪就不会对通过这条边的人发起攻击。当且仅当通过魔法森林的过程中没有任意一条边妖怪小E发起攻击他才能成功找到隐士。

由于携带守护精灵是一件非常麻烦的事,小E想要知道,要能够成功拜访到隐士,最少需要携带守护精灵的总个数。守护精灵总个数为A型守护精灵的个数与B型守护精灵的个数之和。

Input

第1行包含两个整数N,M,表示无向图共有N个节点,M条边。 接下来M行,第行包含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

【样例说明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】

-1

【样例说明2】

小E无法从1号节点到达3号节点,故输出-1。

HINT

2<=n<=50,000

0<=m<=100,000

1<=ai ,bi<=50,000

Source



[Submit][Status][Discuss]


GG。。。。

这题一开始其实就想到了思路,但是由于不会LCT的边权维护。。还是去百度了一下

言归正传

这一题是一道痕经典的LCT维护最小生成树的题目

首先我们枚举每一个a,依次把a值为当前枚举的a的边加入图中

用LCT维护图上的以b为权值的最小生成树森林(注:LCT维护边权可以每一个边建成点,于是转变成维护点权)

这样做完一遍后查询从1到n的路径上的最大值,更新答案

就没啦。。。

但是可能有个问题:如何维护最小生成树森林呢?

我们这样考虑:

对于加入一条边,如果边的两个端点不为同一棵树内,那么直接加进去就好了;

但是如果两个端点在同一棵树内,那么我们加进去这条边势必会产生一个环,这时候我们删掉环上的最大边,即可满足最小生成树的性质

嗯大概就这样

PS.蒟蒻access里忘打maintain调了两个小时。。。

代码:

#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<algorithm>
#include<cstdlib>
#include<stack>
#include<cstring>
#include<cmath>
using namespace std;

typedef long long LL;

const int INF = 2147483647;
const int maxn = 200000;

struct data{
int u,v,a,b;
}e[maxn];

int n,m,tot,ans,p;
int pfa[maxn];
int id[maxn],val[maxn],ch[maxn][2],fa[maxn];
bool rev[maxn],flag,exist[maxn];

inline void maintain(int o)
{
id[o] = o;
if (val[id[o]] < val[id[ch[o][1]]]) id[o] = id[ch[o][1]];
if (val[id[o]] < val[id[ch[o][0]]]) id[o] = id[ch[o][0]];
}

inline void rotate(int x)
{
int o = fa[x],y = fa[o];
pfa[x] = pfa[o]; pfa[o] = 0;
int d = ch[o][1] == x ? 0 : 1;
ch[o][d ^ 1] = ch[x][d]; maintain(o);
if (ch[x][d]) fa[ch[x][d]] = o;
ch[x][d] = o; maintain(x);
fa[o] = x; fa[x] = y;
if (y) ch[y][ch[y][1] == o] = x , maintain(y);
}

inline void reverse(int o)
{
rev[o] ^= 1;
swap(ch[o][1],ch[o][0]);
}

inline void pushdown(int o)
{
if (rev[o])
{
rev[o] = 0;
if (ch[o][1]) reverse(ch[o][1]);
if (ch[o][0]) reverse(ch[o][0]);
}
}

inline void pushdown_root(int x)
{
if (fa[x]) pushdown_root(fa[x]);
pushdown(x);
}

inline void splay(int x)
{
pushdown_root(x);
for (int y = fa[x]; y; rotate(x) , y = fa[x])
if (fa[y]) rotate((ch[y][1] == x) ^ (ch[fa[y]][1] == y) ? x : y);
}

inline void access(int x)
{
for (int u = x,v = 0; u; v = u,u = pfa[u])
{
splay(u);
if (ch[u][1]) fa[ch[u][1]] = 0 , pfa[ch[u][1]] = u;
if (v) fa[v] = u , pfa[v] = 0;
ch[u][1] = v;
maintain(u);
}
}

inline void change(int u)
{
access(u);
splay(u);
reverse(u);
}

inline void cut(int u,int v)
{
change(u);
access(v);
splay(v);
if (ch[v][0]) fa[ch[v][0]] = 0 , ch[v][0] = 0;
maintain(v);
}

inline void join(int u,int v)
{
change(v);
pfa[v] = u;
access(v);
}

inline int find(int u)
{
access(u);
splay(u);
while (ch[u][0]) u = ch[u][0];
return u;
}

inline int query(int u,int v)
{
change(u);
access(v);
splay(v);
return id[v];
}

inline LL getint()
{
LL ret = 0,f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
ret = ret * 10 + c - '0',c = getchar();
return ret * f;
}

inline bool cmp(data a,data b)
{
return a.a < b.a;
}

int main()
{
n = getint(); m = getint();
for (int i = 1; i <= m; i++) e[i] = (data){getint(),getint(),getint(),getint()};
sort(e + 1,e + m + 1,cmp);
ans = INF;
tot = n;
for (int i = 1; i <= m; i++)
{
int u = e[i].u,v = e[i].v,w = e[i].b;
val[++tot] = w;
if (u != v)
{
if (find(u) != find(v))
{
join(u,tot);
join(tot,v);
}
else
{
int pos = query(u,v);
if (val[pos] > w)
{
cut(pos,e[pos - n].u);
cut(e[pos - n].v,pos);
join(u,tot);
join(tot,v);
}
}
}
if (e[i].a != e[i + 1].a)
{
if (find(1) != find(n)) continue;
int pos = query(1,n);
ans = min(ans,val[pos] + e[i].a);
}
}
if (ans == INF) printf("-1\n");
else printf("%d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: