bzoj-3669 魔法森林

题意:

给出一个n个点m条边的无向图,现在要从点1走到点n;

每条边有两个参数a和b,经过这条边必须分别要带不小于a和b的两种权值;

求携带的最小ab权值和,若无解输出-1;

2<=n<=50000,0<=m<=100000

题解:

挺神的一道题。。

考虑路径上最小的b的权值,只需要对b做一次最小生成树就可以了;

但是a的权值怎么办呢?

枚举!

枚举每次经过的最大的a权值,然后求b的最小生成树,更新答案;

但是为了保证最大,要按a权值升序排;

加边就是维护最小生成树啦,LCT!

正确性似乎不用太解释?

然后写一写调一调就好了;

我因为没判断弹的边和要加的边的大小关系WA了一发;

复杂度就是O(mlogm+mlogn)吧;

LCT最近刷到意识模糊。。。

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 51000
#define which(x) (ch[fa[x]][1]==x)
using namespace std;
struct node
{
	int x,y,a,b;
}eg[N<<1];
int fa[N*3],ch[N*3][2],val[N*3],ma[N*3];
bool rt[N*3],rev[N*3];
inline int read()
{
	int x=0;char ch=getchar();
	while(ch<'0'||ch>'9')	ch=getchar();
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x;
}
int cmp(node a,node b)
{
	if(a.a==b.a)
	return a.b<b.b;
	return a.a<b.a;
}
void Pushup(int x)
{
	ma[x]=val[x]>max(val[ma[ch[x][0]]],val[ma[ch[x][1]]])?x:
			(val[ma[ch[x][0]]]>val[ma[ch[x][1]]]?ma[ch[x][0]]:ma[ch[x][1]]);
}
void reverse(int x)
{
	swap(ch[x][0],ch[x][1]);
	rev[x]^=1;
}
void Pushdown(int x)
{
	if(rev[x])
	{
		reverse(ch[x][0]);
		reverse(ch[x][1]);
		rev[x]=0;
	}
}
void down(int x)
{
	if(!rt[x])	down(fa[x]);
	Pushdown(x);
}
void Rotate(int x)
{
	int f=fa[x];
	bool k=which(x);
	if(rt[f])	rt[f]^=rt[x]^=1;
	else	ch[fa[f]][which(f)]=x;
	ch[f][k]=ch[x][!k];
	ch[x][!k]=f;
	fa[ch[f][k]]=f;
	fa[x]=fa[f];
	fa[f]=x;
	Pushup(f);
	Pushup(x);
}
void Splay(int x)
{
	down(x);
	while(!rt[x])
	{
		int f=fa[x];
		if(rt[f])
		{
			Rotate(x);
			return ;
		}
		if(which(x)^which(f))
			Rotate(x);
		else
			Rotate(f);
		Rotate(x);
	}
}
void access(int x)
{
	int y=0;
	while(x)
	{
		Splay(x);
		rt[ch[x][1]]=1,rt[y]=0;
		ch[x][1]=y;
		Pushup(x);
		y=x,x=fa[x];
	}
}
void Mtr(int x)
{
	access(x);
	Splay(x);
	reverse(x);
}
bool judge(int x,int y)
{
	Mtr(x);
	access(y);
	Splay(x);
	while(!rt[y])
		y=fa[y];
	return x==y;
}
void Link(int x,int y)
{
	Mtr(x);
	fa[x]=y;
}
void Cut(int t,int x,int y)
{
	Mtr(t);
	access(x);
	Splay(t);
	rt[ch[t][1]]=1;
	fa[ch[t][1]]=0;
	ch[t][1]=0;
	access(y);
	Splay(t);
	rt[ch[t][1]]=1;
	fa[ch[t][1]]=0;
	ch[t][1]=0;
}
int main()
{
	int n,m,i,j,k,x,y,ans;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;i++)
	{
		eg[i].x=read(),eg[i].y=read(),eg[i].a=read(),eg[i].b=read();
	}
	sort(eg+1,eg+1+m,cmp);
	for(i=1;i<=n;i++)
		rt[i]=1,ma[i]=i;
	for(i=1,ans=0x3f3f3f3f;i<=m;i++)
	{
		x=eg[i].x,y=eg[i].y;
		if(x==y)	continue;
		rt[i+n]=1,val[i+n]=eg[i].b,ma[i+n]=i+n;
		if(judge(x,y))
		{
			if(val[ma[x]]>eg[i].b)
				Cut(ma[x],x,y),Link(i+n,x),Link(i+n,y);
		}
		else
		Link(i+n,x),Link(i+n,y);
		if(judge(1,n))
			ans=min(ans,eg[i].a+val[ma[1]]);
	}
	if(ans==0x3f3f3f3f)
		puts("-1");
	else
		printf("%d\n",ans);
	return 0;
}
时间: 2024-10-27 03:51:50

bzoj-3669 魔法森林的相关文章

bzoj 3669: [Noi2014]魔法森林

3669: [Noi2014]魔法森林 Time Limit: 30 Sec  Memory Limit: 512 MB 动点spfa Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节点1,隐士则住在号节点N.小E需要通过这一片魔法森林,才能够拜访到隐士. 魔法森林中居住了一些妖怪.每当有人经过一条边的时候,这条边上的妖怪就会对其发起攻击.幸运的

【BZOJ】3669: [Noi2014]魔法森林(lct)

http://www.lydsy.com/JudgeOnline/problem.php?id=3669 首先看到题目应该可以得到我们要最小化 min{ max{a(u, v)} + max{b(u, v)} } 两个变量不好做...那么我们约束一个a 即按a从小到大排序,依次加边. 发现当有环出现时,去掉的是环中b最大的边. 证明:因为a是从小到大排序,因此此时答案为 a+max{b(u, v)},显然b越小越好. 然后需要link和cut操作... 脑洞开到这里开不动了...........

BZOJ 3669: [Noi2014]魔法森林( LCT )

排序搞掉一维, 然后就用LCT维护加边MST. O(NlogN) -------------------------------------------------------------------------------------- #include<cstdio> #include<cctype> #include<cstring> #include<algorithm> using namespace std; const int maxn = 1

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需要通过这一片魔法森林,才能够拜访到隐士. 魔法森林中居住了一些妖怪

【BZOJ】【3669】【NOI2014】魔法森林

LCT动态维护MST LCT动态维护MST 我们可以枚举a,然后找从1到n的一条路径使得:这条路径上的b的最大值最小.这个路径肯定在MST上……所以枚举一遍所有的边,动态维护一个关于b值的MST即可. 调了半天没出解的原因: rotate写错了……l=c[y][1]==x 我写成了 l=c[z][1]==y sigh…… 1 /************************************************************** 2 Problem: 3669 3 User

【bzoj 3669】[Noi2014]魔法森林

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

[BZOJ 3669] [Noi2014] 魔法森林 【LCT】

题目链接:BZOJ - 3669 题目分析 如果确定了带 x 只精灵A,那么我们就是要找一条 1 到 n 的路径,满足只经过 Ai <= x 的边,而且要使经过的边中最大的 Bi 尽量小. 其实就是一个按照 Bi 建立的 MST 上 1 到 n 的路径.只能使用 Ai <= x 的边. 那么,如果我们从小到大枚举 x ,这样可以使用的边就不断增加,就是在加边的同时维护 MST ,用 LCT 来做就可以了. 如果新加入一条边 (u, v, w) ,并且原 MST 上 u 到 v 的路径中边权最大

NOI2014 魔法森林

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

【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需要通过这一片魔法森林,才能够拜访到隐士. 魔法森林中居住了一些

BZOJ3669:[NOI2014]魔法森林——题解

http://www.lydsy.com/JudgeOnline/problem.php?id=3669 https://www.luogu.org/problemnew/show/P2387 为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐 士.魔法森林可以被看成一个包含 n 个节点 m 条边的无向图,节点标号为 1,2,3,…,n,边标号为 1,2,3,…,m.初始时小 E 同学在 1 号节点,隐士则住在 n 号节点.小 E 需要通过这一片魔法森林,才能够拜访到隐士. 魔