luoguP2387 [NOI2014]魔法森林

背景:

这一题是 L C T LCT LCT(当然,思路是类似的,可以类比一下,有时间再打),大雾…

题目传送门:

https://www.luogu.org/problemnew/show/P2387

题意:

n n n个点, m m m条边,每条边有两个权值 z 1 , z 2 z_1,z_2 z1,z2,现在让你找到两个值使得它们分别大于从 1 1 1 n n n的某一条路径上的两个值,要求这两个值的最小总和。

思路1:

考虑最短路。
显然最短路无法维护两个值,不妨使得某一个值 z 1 z_1 z1有序(升序),然后根据第二个值做决策。
那么,当每加入一条新边的时候,它就可能会更新答案,不妨在这时跑边两边的两个点的最短路找到在此时最小的 z 2 z_2 z2,然后 n o w z 1 + m i n { z 2 } nowz_1+min\{z_2\} nowz1+min{z2}就是此时的最小答案。
正确性很容易显然,很容易 yy \text y\text y yy

当然,必须要采用动态加点的堆优化+ s p f a / d i j k s t r a spfa/dijkstra spfa/dijkstra,其实我一直分不清这两个的区别,打法类似,所以不知道我的代码是哪一种,欢迎大佬指正。
时间复杂度: Θ ( 玄 学 ) \Theta(玄学) Θ()

代码1:

#include
#include
#include
#include
using namespace std;
struct node3
{
	int id,d;
	friend bool operator <(node3 x,node3 y)
	{
		return x.d>y.d;
	}
};
priority_queue<node3> f;
	int n,m,len=0,ans=2147483647;
	struct node1{int x,y,z1,z2;} d[100010];
	struct node2{int x,y,z,next;} a[200010];
	int last[50010],dis[50010];
	bool bz[50010];
bool cmp(node1 x,node1 y)
{
	return x.z1<y.z1;
}
void ins(int x,int y,int z)
{
	a[++len]=(node2){x,y,z,last[x]}; last[x]=len;
}
int dijkstra(int st1,int st2)
{
	f.push((node3){st1,dis[st1]});
	f.push((node3){st2,dis[st2]});
	while(!f.empty())
	{
		int x=f.top().id;
		f.pop();
		for(int i=last[x];i;i=a[i].next)
		{
			int y=a[i].y;
			if(dis[y]>max(dis[x],a[i].z))
			{
				dis[y]=max(dis[x],a[i].z);
				if(!bz[y]) f.push((node3){y,dis[y]}),bz[y]=true;
			}
		}
		bz[x]=false;
	}
	return dis[n];
}
int main()
{
	scanf("%d %d",&n,&m);
	for(int i=1;i<=m;i++)
		scanf("%d %d %d %d",&d[i].x,&d[i].y,&d[i].z1,&d[i].z2);
	sort(d+1,d+m+1,cmp);
	memset(dis,63,sizeof(dis));
	dis[1]=0;
	bool flag=false;
	for(int i=1;i<=m;i++)
	{
		if(d[i].z1>=ans) break;
		if(d[i].x==d[i].y) continue;
		ins(d[i].x,d[i].y,d[i].z2);
		ins(d[i].y,d[i].x,d[i].z2);
		if(d[i].x==1||d[i].y==1) flag=true;
		if(flag) ans=min(ans,d[i].z1+dijkstra(d[i].x,d[i].y));
	}
	printf("%d",ans<(int)1e9?ans:-1);
}

思路2:

L C T LCT LCT维护最小生成树的思想。
类似的,我们按照 z 1 z_1 z1排序,在 L C T LCT LCT中维护 z 2 z_2 z2的值,然后就可以将时间复杂度稳定在 Θ ( n l o g n ) \Theta(nlogn) Θ(nlogn)

代码:

#include
#include
#include
using namespace std;
	struct node1{int d,fa,ma,pos,lazy,son[2];} tr[300010];
	struct node2{int x,y,z1,z2;} a[300010],d[300010];
	int n,m,ans=2147483647;
bool cmp(node2 x,node2 y)
{
	return x.z1<y.z1;
}
bool isroot(int x)
{
	return tr[tr[x].fa].son[0]!=x&&tr[tr[x].fa].son[1]!=x;
}
bool isson(int x)
{
	return x==tr[tr[x].fa].son[1];
}
void change(int x)
{
	if(!x)return;
	swap(tr[x].son[0],tr[x].son[1]);
	tr[x].lazy^=1;
}
void pushup(int x)
{
	int lc=tr[x].son[0],rc=tr[x].son[1];
	tr[x].ma=tr[x].d,tr[x].pos=x;
	if(tr[lc].ma>tr[x].ma) tr[x].ma=tr[lc].ma,tr[x].pos=tr[lc].pos;
	if(tr[rc].ma>tr[x].ma) tr[x].ma=tr[rc].ma,tr[x].pos=tr[rc].pos;
}
void pushdown(int x)
{
	if(!tr[x].lazy) return;
	change(tr[x].son[0]),change(tr[x].son[1]);
	tr[x].lazy=0;
}
void rot(int x)
{
	int w=isson(x),y=tr[x].fa,yy=tr[y].fa;
	tr[y].son[w]=tr[x].son[w^1];
	if(tr[y].son[w]) tr[tr[y].son[w]].fa=y;
	tr[x].fa=yy;
	if(!isroot(y)) tr[yy].son[isson(y)]=x;
	tr[x].son[w^1]=y;tr[y].fa=x;
	pushup(y);
}
int sta[300010];
int top;
void splay(int x)
{
	sta[top=1]=x;
	for(int i=x;!isroot(i);i=tr[i].fa)
		sta[++top]=tr[i].fa;
	while(top) pushdown(sta[top--]);
	for(int y=tr[x].fa;!isroot(x);rot(x),y=tr[x].fa)
		if(!isroot(y)) isson(x)^isson(y)?rot(x):rot(y);
	pushup(x);
}
void access(int x)
{
	for(int y=0;x;y=x,x=tr[x].fa)
		splay(x),tr[x].son[1]=y,pushup(x);
}
int findroot(int x)
{
	access(x);splay(x);
	while(tr[x].son[0]) x=tr[x].son[0];
	//splay(x);
	return x; 
}
void makeroot(int x)
{
	access(x);splay(x);change(x);
}
void link(int x,int y)
{
	makeroot(x);
	if(findroot(y)!=x) tr[x].fa=y;
}
void split(int x,int y)
{
	makeroot(x);access(y);splay(y);
}
void del(int x,int y)
{
	split(x,y);
	tr[x].fa=tr[y].son[0]=0,pushup(x),pushup(y);
}
int query(int x,int y)
{
	split(x,y);
	return tr[y].pos;
}
int main()
{
	scanf("%d %d",&n,&m);
	for(int i=1;i<=m;i++)
		scanf("%d %d %d %d",&d[i].x,&d[i].y,&d[i].z1,&d[i].z2);
	sort(d+1,d+m+1,cmp);
	for(int i=n+1;i<=n+m;i++)
		a[i]=d[i-n];
	for(int i=0;i<=(n<<1);i++)
		tr[i]=(node1){0,0,0,i,0,0,0};
	for(int i=n+1;i<=n+m;i++)
	{
		if(findroot(a[i].x)!=findroot(a[i].y))
		{
			tr[i].d=a[i].z2;
			link(a[i].x,i);link(a[i].y,i);
		}
		else
		{
			int pos=query(a[i].x,a[i].y);
			if(pos>n&&a[i].z2<a[pos].z2)
			{
				del(a[pos].x,pos);del(a[pos].y,pos);
				tr[i].d=a[i].z2;
				link(a[i].x,i);link(a[i].y,i);
			}
		}
		if(findroot(1)==findroot(n)) ans=min(ans,a[i].z1+a[query(1,n)].z2);
	}
	printf("%d",findroot(1)==findroot(n)?ans:-1);
}

你可能感兴趣的:(#,最短路,#,spfa,#,最小生成树,#,LCT)