BZOJ 3669 [Noi2014]魔法森林 ——SPFA / Link-Cut Tree

【题目分析】

    大意就是有一张图,边权有两个值,ai和bi

    找到一条路径,使得路径上的max(ai)+max(bi)最小。

    遇到有两个权值或者多个权值的时候,如果他们互相影响,试着用分块搞一搞。

    如果互不影响,先用排序搞掉一维。

    这显然a与b分开计算,直接按照ai排序。

    之后不断加入,然后计算在bi上的最短路,更新答案即可。

    SPFA代码相当好写(复杂度一直是玄学如果重边比较多,几乎可以达到nm)

    这么玄学的复杂度居然AC了,真实神奇。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>

#include <map>
#include <set>
#include <queue>
#include <string>
#include <iostream>
#include <algorithm>

using namespace std;

#define maxn 300005
#define inf 0x3f3f3f3f
#define F(i,j,k) for (int i=j;i<=k;++i)
#define D(i,j,k) for (int i=j;i>=k;--i)

void Finout()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);
    #endif
}

int Getint()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0'&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}

int n,m,dis[maxn],ans=inf;
struct edge{int l,r,a,b;}e[maxn];
bool cmp(edge x,edge y)
{return x.a<y.a;}
int h[maxn<<1],ne[maxn<<1],to[maxn<<1],w[maxn<<1],en=0;

void add(int a,int b,int c)
{
	to[en]=b; w[en]=c; ne[en]=h[a]; h[a]=en++;
	to[en]=a; w[en]=c; ne[en]=h[b]; h[b]=en++;
}

int inq[maxn];
queue <int> q;

void SPFA(int S)
{
	while (!q.empty()) q.pop();
	q.push(S);
	while (!q.empty())
	{
		int x=q.front();q.pop();inq[x]=0;
//		cout<<"on "<<x<<endl; 
		for (int i=h[x];i>=0;i=ne[i])
		{
			if (dis[to[i]]>max(dis[x],w[i]))
			{
				dis[to[i]]=max(dis[x],w[i]);
				if (!inq[to[i]])
				{
					inq[to[i]]=1;
					q.push(to[i]);
				}
			}
		}
	}
}

int main()
{
    Finout();
    n=Getint(); m=Getint();
    F(i,1,m)
    {
    	e[i].l=Getint();
    	e[i].r=Getint();
    	e[i].a=Getint();
    	e[i].b=Getint();
	}
	sort(e+1,e+m+1,cmp);
	memset(h,-1,sizeof h);
	memset(dis,0x3f,sizeof dis);
	dis[1]=0;
	F(i,1,m)
	{
		add(e[i].l,e[i].r,e[i].b);
		SPFA(e[i].l);SPFA(e[i].r);
		ans=min(ans,dis[n]+e[i].a);
	}
	if (ans==inf) cout<<"-1"<<endl;
	else cout<<ans<<endl;
}

    之后想一想,用LCT去维护边权貌似复杂度更好。

    也是按照ai排序,然后加入边,如果构成环,就删去权值最大的一个。

    然后统计答案。

    维护边权的时候,直接维护比较难,可以把原图边和点都看作点,然后互相连接,这样就只有点权了。

    写起来也很爽。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>

#include <map>
#include <set>
#include <queue>
#include <string>
#include <iostream>
#include <algorithm>

using namespace std;

#define maxn 500005
#define inf 0x3f3f3f3f
#define F(i,j,k) for (int i=j;i<=k;++i)
#define D(i,j,k) for (int i=j;i>=k;--i)

void Finout()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);
    #endif
}

int Getint()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0'&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}

int mx[maxn],v[maxn],n,m,ans=inf,sta[maxn],top=0;
int fa[maxn],ch[maxn][2],rev[maxn],val[maxn];
struct edge{int u,v,a,b;}e[maxn];
bool cmp(edge x,edge y){return x.a<y.a;}

bool isroot(int x)
{return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}

void update(int x)
{
	mx[x]=x;
	if (val[mx[ch[x][0]]]>val[mx[x]]) mx[x]=mx[ch[x][0]];
	if (val[mx[ch[x][1]]]>val[mx[x]]) mx[x]=mx[ch[x][1]];
}

void pushdown(int x)
{
	if (rev[x])
	{
		rev[x]^=1;
		rev[ch[x][0]]^=1;
		rev[ch[x][1]]^=1;
		swap(ch[x][0],ch[x][1]);
	}
}

void rot(int x)
{
	int y=fa[x],z=fa[y],l,r;
	if (ch[y][0]==x) l=0; else l=1;
	r=l^1;
	if (!isroot(y))
	{
		if (ch[z][0]==y) ch[z][0]=x;
		else ch[z][1]=x;
	}
	fa[x]=z; fa[y]=x; fa[ch[x][r]]=y;
	ch[y][l]=ch[x][r]; ch[x][r]=y;
	update(y);update(x);
}

void splay(int x)
{
	top=0;sta[++top]=x;
	for (int i=x;!isroot(i);i=fa[i]) sta[++top]=fa[i];
	while (top) pushdown(sta[top--]);
	
	while (!isroot(x))
	{
		int y=fa[x],z=fa[y];
		if (!isroot(y))
		{
			if (ch[y][0]==x^ch[z][0]==y) rot(y);
			else rot(x);
		}
		rot(x);
	}
}

void access(int x)
{
	for (int t=0;x;t=x,x=fa[x])
		splay(x),ch[x][1]=t,update(x);
}

void makeroot(int x)
{
	access(x);
	splay(x);
	rev[x]^=1;
}

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

void link(int x,int y)
{
	makeroot(x);
	fa[x]=y;
}

void cut(int x,int y)
{
	makeroot(x);
	access(y);
	splay(y);
	if (ch[y][0]==x)
	{
		ch[y][0]=x;
		fa[x]=0;
	}
}

int query(int x,int y)
{
	makeroot(x);
	access(y);
	splay(y);
	return mx[y];
}

int main()
{
    Finout();
    val[0]=-inf;
    n=Getint();m=Getint();
    F(i,1,n) val[i]=-inf;
    F(i,1,m)
    {
    	e[i].u=Getint();
    	e[i].v=Getint();
    	e[i].a=Getint();
    	e[i].b=Getint();
	}
	sort(e+1,e+m+1,cmp);
	F(i,1,m)
	{
		int l=e[i].u,r=e[i].v,a=e[i].a,b=e[i].b;
		int flag=0;
//		cout<<l<<" "<<r<<" "<<a<<" "<<b<<endl;
		if (find(l)==find(r))
		{
			int tmp=query(l,r);
			if (val[tmp]>b)
			{
//				cout<<"cut "<<tmp<<" "<<e[tmp-n].v<<" "<<e[tmp-n].u<<endl;
				cut(tmp,e[tmp-n].v);
				cut(tmp,e[tmp-n].u);
				flag=1;
			}
			else
			{
//				cout<<"cal"<<endl;
				if (find(1)==find(n))
				{
					ans=min(ans,a+val[query(1,n)]);
					continue;
				}
			}
		}
		else flag=1;
		if (flag)
		{
//			cout<<"link"<<endl;
			link(l,n+i);link(r,n+i);
			val[n+i]=e[i].b;
			mx[n+i]=n+i;
		}
		if (find(1)==find(n))
		{
//			cout<<"cal"<<endl;
			ans=min(ans,a+val[query(1,n)]);
		}
	}
	if (ans==inf) cout<<"-1"<<endl;
	else cout<<ans<<endl;
}

  

转载于:https://www.cnblogs.com/SfailSth/p/6294832.html

原文地址:https://www.cnblogs.com/twodog/p/12141385.html