zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Android学习笔记(四) 定时器Timer
    Android学习笔记(三) UI布局
    JAVA 抽象类、接口
    JAVA 类与对象
    React使用Ant Design Mobile结合rc-form进行表单验证
    JS学习笔记--为同种类型控件添加事件,无法应用循环变量的解决办法
    CSS学习笔记--圣杯布局与双飞翼布局
    CSS学习笔记--flex弹性布局
    CSS学习笔记--浮动元素由于浏览器页面缩小而被挤到下面的解决方法
    CSS学习笔记--导航栏元素由于页面缩小而被挤到下一行的解决方法
  • 原文地址:https://www.cnblogs.com/twodog/p/12141386.html
Copyright © 2011-2022 走看看