【BZOJ3669】【NOI2014】魔法森林 (spfa动态队列加点算法)

3669: [Noi2014]魔法森林

Time Limit: 30 Sec  Memory Limit: 512 MB

Submit: 254  Solved: 140

[Submit][Status]

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

题意:两个点权的最短路。

题解:正解(可能比歪解慢一些)linkcut tree,歪解 spfa枚举权值a求b的单权最短路。

spfa实现:枚举a权值更新ans得到答案。

优化:     1 . 不对dist数组进行更新(单调性保证)(否则狂TLE)

2 . 随a权值递增而加边,同时在函数外让点入队 (否则狂WA)

3 . 对a权值排序然后进行枚举。(否则有可能常数TLE)

以下为非必须优化:

4 . 对边进行边权排序,加边时可以保证线性时间复杂度

5 . 对SPFA(Dijsktra)进行优先队列优化(堆优化)。(因为不是最短路,而是边权值的极值,所以优化效果不明显,甚至可能更慢)

6 . 枚举时对a权值去重

7 . 对源点(1)的出边求最小a权值mina,对汇点(n)的入边求最小a权值minb,然后在枚举a权值跑b的单权值spfa时可以在枚举值<max(mina,minb)时不进行spfa,直接略过!

注意:    1 . dist要在主函数枚举a权值前memset 同时dist[s](dist[1])=0;

2 . 别把边漏加了。

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define N 50100
#define M 201000
using namespace std;
struct Syndra
{
	int u,v,len,next;
	bool operator < (const Syndra& a)const
	{return a.next>next;}
}ep[M],e[M];
struct Fiona
{
	int f,v;
	Fiona(int a,int b):f(b),v(a){}
	bool operator < (const Fiona& a)const
	{return a.f<f;}
};
int head[N],cnt,n,m,limit;/*for Syndra*/
int lsh[M];/*for 离散化*/
int dist[N],in[N];/*for spfa*/
void add(int u,int v,int len)
{
	cnt++;
	e[cnt].v=v;
	e[cnt].len=len;
	e[cnt].next=head[u];
	head[u]=cnt;
}
void ad(int u,int v,int lena,int lenb)
{
	++cnt;
	ep[cnt].u=u;
	ep[cnt].v=v;
	ep[cnt].next=lena;
	ep[cnt].len=lenb;
}
priority_queue<Fiona>q;
void spfa()
{
	int i,u,v;
	Fiona X(0,0);
	while(!q.empty())
	{
		X=q.top();
		q.pop();
		u=X.v;
		in[u]=0;
		for(i=head[u];i;i=e[i].next)
		{
			v=e[i].v;
			if(dist[v]>max(dist[u],e[i].len))
			{
				dist[v]=max(dist[u],e[i].len);
				if(!in[v])
				{
					in[v]=1;
					q.push(Fiona(v,dist[v]));
				}
			}
		}
	}
}

int main()
{
//	freopen("forest.in","r",stdin);
//	freopen("forest.ans","w",stdout);
	int i,j,k,num;
	int a,b,c,d;
	int ans,ma,mb;
	int u,v;
	scanf("%d%d",&n,&m);
	ma=mb=0x3f3f3f3f;
	for(i=1;i<=m;i++)
	{
		scanf("%d%d%d%d",&a,&b,&c,&d);
		ad(a,b,c,d);
		lsh[i]=c;
		if(a==1||b==1)ma=min(ma,c);
		if(a==n||b==n)mb=min(mb,c);
	}
	sort(lsh+1,lsh+m+1);
	sort(ep+1,ep+m+1);
	ans=0x3f3f3f3f;
	ma=max(ma,mb);
	memset(dist,0x3f,sizeof(dist));
	dist[1]=0;
	for(num=cnt=1,i=1;i<=m;i++)
	{
		for(;num<=m;num++)
		{
			if(ep[num].next>lsh[i])break;
			u=ep[num].u;v=ep[num].v;
			add(u,v,ep[num].len);
			add(v,u,ep[num].len);
			q.push(Fiona(u,dist[u])),in[u]=1;
			q.push(Fiona(v,dist[v])),in[v]=1;
		}
		if(lsh[i]<ma)continue;
		if(lsh[i]==lsh[i+1])continue;
		limit=lsh[i];spfa();
		ans=min(ans,dist[n]+limit);
	}
	if(ans==0x3f3f3f3f)printf("-1");
	else printf("%d\n",ans);
	return 0;
}
时间: 2024-11-05 20:42:15

【BZOJ3669】【NOI2014】魔法森林 (spfa动态队列加点算法)的相关文章

[bzoj3669][Noi2014]魔法森林_LCT_并查集

魔法森林 bzoj-3669 Noi-2014 题目大意:说不明白题意系列++……题目链接 注释:略. 想法:如果只有1个参量的话spfa.dij什么的都上来了. 两个参量的话我们考虑,想将所有的边按照a排序. 如果两个点:它们之间有两条路径,有一条比另一条劣. 那么我们完全可以将另一条弄掉. 排序之后维护生成树. LCT的点维护的是实子树中第二参量的最大值. 如果当前边连接的两点之前不连通,直接连上. 如果联通,我们判断新边的第二参量和两点之间splay的最大参量之间的关系. 如果新边的第二参

BZOJ 3669 NOI2014 魔法森林 SPFA

题目大意: 给定一个无向图,每条边有两个权值ai和bi,从1走到N,设路径上a权的最大值为A,b权的最大值为B,求A+B的最小值 首先这题如果只有一个权值就是水题无误--但是多了个权值之后我们就要好好考虑一下了 我们对a排序,枚举a,对于每一次枚举求b权最大值的最小值即可 跑M遍SPFA肯定超时无误 网上很多人写了LInk-Cut-Tree维护动态最小生成树 我的LCT没写明白 就去写了SPFA.... 这里要用的SPFA的动态加点(边)法 我们每加一条边 就把边的两端点入队 继续SPFA 不用

沉迷Link-Cut tree无法自拔之:[BZOJ3669][Noi2014] 魔法森林

来自蒟蒻 \(Hero \_of \_Someone\) 的 \(LCT\) 学习笔记 $ $ 有一个很好的做法是 \(spfa\) ,但是我们不聊 \(spfa\) , 来聊 \(LCT\) \(LCT\) 做法跟 \(spfa\) 的做法其实有点像, 先将所有的边按 \(a\) 的值从小到大排, 再以 \(b\) 的值为边权来动态的维护最小生成树, 答案即为 当前插入边的 \(a\) 值加上最小生成树中的最大边权 的最小值 $ $ 此外, 用 \(LCT\) 维护 \(MST\) , 就是在

BZOJ3669: [Noi2014]魔法森林

传送门 高级数据结构学傻系列 正解似乎是最短路xjb搞,但是用LCT瞎搞搞也是很吼啊. 从贪心开始,按照每条边a的大小随意sort一下. 对于每个边,我们check两点的联通性,如果联通的话取b最大的值,如果大于当前边的b的话就就删除最大边,把这条边加进去. 如果不连通的话直接添加即可. LCT滋次这些操作,所以大力LCT即可. //BZOJ 3669 //by Cydiater //2017.2.16 #include <iostream> #include <queue> #i

bzoj3669: [Noi2014]魔法森林 lct

记得去年模拟赛的时候好像YY出二分答案枚举a,b的暴力,过了55欸 然后看正解,为了将两维变成一维,将a排序,模拟Kruskal的加边过程,同时维护1到n的最大值,加入一条边e(u,v,a,b)时有以下两种情况: 1) 若u,v已连通,则找出u->v上最大的b',若b<b',则替换之,同时更新答案,注意e一定经过1->n,因为去掉b'所在边时1,n一定不连通,若加上e后1,n连通,则必经过e,由于a是有序的,所以a是路径上最大的a,用a+MAX_b[1->n]更新答案即可. 2)否

BZOJ3669 NOI2014魔法森林

按a从小到大排序,然后按b建图. 每次只需要找1~n中最大的b加当前的a计算答案即可. 这里还有一个小操作就是化边为点,把一条边的边权看做一个点的点权然后多连两条边. 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=4e5+10; 4 int f[N],fa[N],ma[N],pos[N],c[N][2],rev[N],s[N],n,m,ans=2e9,w[N]; 5 struct node{ 6 int x,y,a

bzoj3669 [Noi2014]魔法森林——LCT

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3669 第一道LCT! 主要是看这个博客理解学LCT板子:https://blog.csdn.net/yxuanwkeith/article/details/50991326 关于这道题,又看了看这个博客:https://blog.csdn.net/clove_unique/article/details/51317842 然后努力抄写了半天,成功AC! 代码中 //// 的地方是我还有点不

【BZOJ3669】[Noi2014]魔法森林 LCT

[BZOJ3669][Noi2014]魔法森林 Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节点1,隐士则住在号节点N.小E需要通过这一片魔法森林,才能够拜访到隐士. 魔法森林中居住了一些妖怪.每当有人经过一条边的时候,这条边上的妖怪就会对其发起攻击.幸运的是,在号节点住着两种守护精灵:A型守护精灵与B型守护精灵.小E可以借助它们的力量,达到自

bzoj 3669: [Noi2014]魔法森林 动态树

3669: [Noi2014]魔法森林 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 363  Solved: 202[Submit][Status] Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节点1,隐士则住在号节点N.小E需要通过这一片魔法森林,才能够拜访到隐士. 魔法森林中居住了一些妖怪