• dijkstra与他的优化!!!


    @

    SPFA已死,有事烧纸

    其实我本人也是一个SPFA的忠诚用户,至少我的最少费用最大流都是用SPFA打的。但是,就在最近,发生了一个天大的丑闻!一个大佬竟将SPFA卡死!!!还有千千万万的SPFA站起来!!!

    不过SPFA其实还是可以用来拿一定的分数,比如一些很神奇的优化。

    这时,我们的(dijkstra)便一下子火了!

    而且(dijkstra)也可以加优化,最快可以到达(O(nlogn+m)),吊打SPFA?

    Dijkstra

    注:Dijkstra不能处理负边的情况。

    在这里插入图片描述
    首先,我们有四个点,1号点的点权为0,那么我们就用他更新其他的点。

    在这里插入图片描述

    那么现在在白色点里面,4号点是最小的,我们继续用4号点更新。

    在这里插入图片描述

    继续:

    在这里插入图片描述

    那么我们就可以得到一段代码。

    bool bk[21000];
    int d[21000];
    d[0]=inf;
    for(int i=1;i<n;i++)
    {
        int  id=0;
        for(int  i=1;i<=n;i++)
        {
        	if(!bk[i] && d[i]<d[id])id=i;
    	}//找最小 
    	bk[id]=true;
        for(int  k=last[id];k;k=a[k].next)
        {
        	int  c=a[k].c;//边权 
            if(d[a[k].y/*边的另外一个终点*/]>d[id]+c)d[a[k].y]=d[id]+c;
        }
    }
    

    当然,这种做法也是显而易见的正确了,在无负边的情况下当前距离最短的没被选过的点肯定不能被其他没被选过的点更新,实则就是加了贪心思想。

    但是因为裸写的复杂度为(O(n^{2}+m)),在找最小值方面较慢,所以也不行,需要用数据结构优化。

    配对堆

    引言

    这里的配对堆不是pb_ds里面的!!!!!!!!!!!!!!!!!!!!

    在无尽地堆海世界里面,有个叫斐波纳契堆的玩意,修改插入(O(1)),弹出堆顶(O(logn)),而二项堆则较为鸡肋,而这两个常数都大的一匹(代码也长)!!!

    而二叉堆、优先队列(STL,开O2的话配对堆的时间和他差不了多少)、zkw线段树(不是堆,但这个常数真的小,配对堆跟他比还是慢一点),但是由于修改为(O(logn)),导致时间复杂度变为(O(n+mlogn))(优先队列的一般写法是(O(n+logm)),因为不支持修改),但是常数小。

    有没有综合一点的一个数据结构呢?

    这是,配对堆便闪闪发光了,因为代码简洁和具备斐波纳契堆一样的复杂度,以及常数较小,被世人所赞叹,不过,还是存在许多不确定性的,毕竟没有完美的事物。

    讲解

    配对堆实质上是一棵树,用边目录进行连接

    玩一玩排序

    由于这个写的是排序,所以就比较简单,但是在优化Dij时因为要修改,为防止内存大和速度快,改了一些内容,当然,等你学会了排序后,这个差不多就会了。

    下面讲

    合并

    两个堆的堆头,哪个小哪个为根,另外一个认他为父亲,就这么粗暴!!!

    inline  int  merge(int  x,int  y){v[x]>v[y]?(x^=y^=x^=y):0;inz(x,y);return  x;}
    

    修改

    就是我与父亲断绝关系,然后我修改我自己的值,将我与我的子树与root合并。

    按我的理解是(O(1)),而且对后面(pop)的影响也只是(1)(2)次的影响,不知道为什么官方说是(O(logn)),当然,此篇文章将按(O(1))来算。

    当然,这样只能改小,不能改大,改大的话可以断绝关系后pop一下然后再合并回来,(O(logn))的时间。

    void  change(int  id,LL  x){v[id]=x;fa[id]=0;root=(root==id?id:merge(root,id));/*这条边就浪费了*/}//改变边权,这题不用 
    

    当然,边就浪费了,在后面会有一个回收的版本,但是慢一点,内存会小。

    弹出堆顶pop

    这么随便的一个数据结构为什么复杂度这么优秀,全看核心操作,pop(),将堆顶的所有儿子按一定顺序合并起来,然后弹出堆顶,而合并的顺序也是有讲究的,不是一个接一个,而是两两合并,这样每个点的儿子最多只有(logn)个,而配对堆时间复杂度最优秀的时候则是一条链的时候,排序都排序好了!

    给个图好理解:

    在这里插入图片描述

    不是树,是合并的过程。

    这个时候也许我们的儿子合并代码会这样写:

    //vip为list中点的数量,list为儿子
    int  p=vip/2;
    while(vip>1)
    {
        for(int  i=1;i<=p;i++)list[i]=merge(list[i],list[i+p]);
        (vip&1)?list[++p]=list[vip]:1;vip=p;p=vip/2;
    }
    

    但是大佬强就强在了这里(可以点击):

    int  p=1;
    while(p<vip)list[++vip]=merge(list[p],list[p+1]),p+=2;
    

    简单,明了!!!!

    那么给出pop完整的代码:

    int  list[M],vip;
    inline  void  pop()
    {
        vip=0;
        for(int  k=las[root];k;k=e[k].next)
        {
            zjj.save(k)/*回收边的编号*/,(fa[e[k].y]==root?(be[list[++vip]=e[k].y]=fa[e[k].y]=0):0);
        }
        int  p=1;
        while(p<vip)
        {
            list[++vip]=merge(list[p],list[p+1]),p+=2;
        }
        las[root]=0;root=list[vip];
    }
    

    代码

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #define  N  110000
    using  namespace  std;
    typedef  long  long  LL;
    class  dui
    {
        public:
            struct  neicun
            {
                int  a[N],ts,len;
                neicun(){ts=0;len=0;}
                int  get(){return  ts?a[ts--]:++len;}
                void  save(int  id){a[++ts]=id;}//存边 
            }zjj;//内存池 
            int  root,fa[N]/*父亲*/;
            LL  v[N];//权值 
            struct  node
            {
                int  y,next;
            }a[N];int  len,last[N];
            void  ins(int  x,int  y){int  now=zjj.get();a[now].y=y;a[now].next=last[x];last[x]=now;fa[y]=x;}//建边 
            int  merge(int  x,int  y){v[x]>v[y]?(x^=y^=x^=y):0;ins(x,y);return  x;}//合并 
            void  push(int  id,LL  x){v[id]=x;root=(root?merge(root,id):id);}//推入堆中 
            void  change(int  id,LL  x){v[id]=x;fa[id]=0;root=(root==id?id:merge(root,id));/*这条边就浪费了*/}//改变边权,这题不用 
            LL  top(){return  v[root];}
            int  list[210000],vip;
            void  pop()//踢出堆顶 
            {
                vip=0;
                for(int  k=last[root];k;k=a[k].next)zjj.save(k),(fa[a[k].y]==root?fa[list[++vip]=a[k].y]=0:0);
                int  p=1;
                while(p<vip)list[++vip]=merge(list[p],list[p+1]),p+=2;
                last[root]=0;root=list[vip];
            }
    }zs;
    int  n;
    int  main()
    {
        scanf("%d",&n);
        for(int  i=1;i<=n;i++)
        {
            LL  x;scanf("%lld",&x);
            zs.push(i,x);
        }
        for(int  i=1;i<n;i++)
        {
            printf("%lld ",zs.top());
            zs.pop();
        }
        printf("%lld
    ",zs.top());
        return  0;
    }
    

    结合!

    1

    一道恶心的题目

    那么,这道题目看着很简单,其实很恶心,因为如果你堆里面的边目录开到10000000,内存就炸裂,所以必须要将change的边回收,同时,一开始不要建所有的点,都是一些比较重要的细节。

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #define  N  1000002
    #define  NN  2000002
    #define  M  10000002
    using  namespace  std;
    typedef  long  long  LL;
    //堆 
    bool  type[N];
    struct  neicun
    {
        int  qaq[N],ts,len;
        neicun(){ts=0;len=0;}
        int  get(){return  ts?qaq[ts--]:++len;}
        void  save(int  id){qaq[++ts]=id;}
    }zjj;//内存 
    int  root;
    LL  v[N];
    struct  node
    {
        int  x,y,next,step;//双向链表 
    }e[N];int  las[N],be[N]/*自己连向父亲的那条边*/,fa[N];
    inline  void  inz(int  x,int  y){int  now=zjj.get();e[now].x=x;e[now].y=y;e[now].next=las[x];e[now].step=0;e[las[x]].step=now;las[x]=be[y]=now;fa[y]=x;}
    inline  int  merge(int  x,int  y){v[x]>v[y]?(x^=y^=x^=y):0;inz(x,y);return  x;}
    inline  void  push(int  id,LL  x){v[id]=x;root=(root?merge(root,id):id);}
    inline  void  put(int  id){e[e[id].next].step=e[id].step;e[e[id].step].next=e[id].next;}//删点 
    inline  void  change(int  id,LL  x)
    {
    	if(!type[id])
    	{
    		type[id]=true;
    		push(id,x);
    		return  ;
    	}//一开始不要提前建所有的点 
    	v[id]=x;
    	if(be[id]){put(be[id]);be[id]==las[fa[id]]?las[fa[id]]=e[be[id]].next:0;zjj.save(be[id]);fa[id]=be[id]=0;}
    	root=(root==id?id:merge(root,id));
    }
    inline  int  top(){return  root;}
    int  list[NN],vip;
    inline  void  pop()
    {
        vip=0;
        for(int  k=las[root];k;k=e[k].next)
    	{
    		zjj.save(k),(fa[e[k].y]==root?(be[list[++vip]=e[k].y]=fa[e[k].y]=0):0);
    	}
        int  p=1;
        while(p<vip)
    	{
    		list[++vip]=merge(list[p],list[p+1]),p+=2;
    	}
        las[root]=0;root=list[vip];
    }
    //
    struct  bian
    {
        int  y,next,c;
    }a[M];int  len,last[N];//边目录 
    inline  void  ins(int  x,int  y,int  c){len++;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;}
    int  n,m;
    int  main()
    {
        scanf("%d%d",&n,&m);
        int  o1;LL  o2,o3,o4,o5,o6;scanf("%d%lld%lld%lld%lld%lld",&o1,&o2,&o3,&o4,&o5,&o6);
        LL  x=0,y=0;//建边 
        for(int  i=1;i<=o1;i++)
        {
        	x=(x*o2+o3)%o6;y=(y*o4+o5)%o6;
        	int  a=int(x%(LL)n+1),b=int(y%LL(n)+1);
        	if(a>b)a^=b^=a^=b;
        	ins(a,b,1e8-100*a);
    	}//
        for(int  i=m-o1;i>=1;i--)
        {
            int  x,y,c;scanf("%d%d%d",&x,&y,&c);
            ins(x,y,c);
        }
        for(int  i=1;i<=n;i++)v[i]=LL(99999999999999999);
        change(1,0);
        for(int  i=1;i<n;i++)
        {
            int  id=top();pop();
            for(int  k=last[id];k;k=a[k].next)
            {
            	LL  c=a[k].c;
                if(v[a[k].y]>v[id]+c)change(a[k].y,v[id]+c);
            }
        }
        printf("%lld
    ",v[n]);
        return  0;
    }
    

    虽然速度慢了点。

    同时,这道题数据很水。

        LL  x=0,y=0;//建边 
        for(int  i=1;i<=o1;i++)
        {
        	x=(x*o2+o3)%o6;y=(y*o4+o5)%o6;
        	int  a=int(x%(LL)n+1),b=int(y%LL(n)+1);
        	if(a>b)a^=b^=a^=b;
        	ins(a,b,1e8-100*a);
    	}
    

    这一段建边可以不用,也可以A,同时你的速度也会快几十秒!!!

    QAQ。

    2

    简单的题

    这道题目就很简单了吗!

    // luogu-judger-enable-o2
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #define  N  100005
    #define  M  200005
    using  namespace  std;
    typedef  long  long  LL;
    bool  type[N];
    struct  neicun
    {
        int  qaq[N],ts,len;
        neicun(){ts=0;len=0;}
        int  get(){return  ts?qaq[ts--]:++len;}
        void  save(int  id){qaq[++ts]=id;}
    }zjj;
    int  root;
    LL  v[N];
    struct  node
    {
        int  x,y,next,step;
    }e[N];int  las[N],be[N],fa[N];
    inline  void  inz(int  x,int  y){int  now=zjj.get();e[now].x=x;e[now].y=y;e[now].next=las[x];e[now].step=0;e[las[x]].step=now;las[x]=be[y]=now;fa[y]=x;}
    inline  int  merge(int  x,int  y){v[x]>v[y]?(x^=y^=x^=y):0;inz(x,y);return  x;}
    inline  void  push(int  id,LL  x){v[id]=x;root=(root?merge(root,id):id);}
    inline  void  put(int  id){e[e[id].next].step=e[id].step;e[e[id].step].next=e[id].next;}
    inline  void  change(int  id,LL  x)
    {
        if(!type[id])
        {
            type[id]=true;
            push(id,x);
            return  ;
        }
        v[id]=x;
        if(be[id]){put(be[id]);be[id]==las[fa[id]]?las[fa[id]]=e[be[id]].next:0;zjj.save(be[id]);fa[id]=be[id]=0;}
        root=(root==id?id:merge(root,id));
    }
    inline  int  top(){return  root;}
    int  list[M],vip;
    inline  void  pop()
    {
        vip=0;
        for(int  k=las[root];k;k=e[k].next)
        {
            zjj.save(k),(fa[e[k].y]==root?(be[list[++vip]=e[k].y]=fa[e[k].y]=0):0);
        }
        int  p=1;
        while(p<vip)
        {
            list[++vip]=merge(list[p],list[p+1]),p+=2;
        }
        las[root]=0;root=list[vip];
    }
    struct  bian
    {
        int  y,next;
        LL  c;
    }a[M];int  len,last[N];
    inline  void  ins(int  x,int  y,LL  c){len++;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;}
    int  n,m,st;
    LL  d[N];
    int  main()
    {
        scanf("%d%d%d",&n,&m,&st);
        for(int  i=1;i<=m;i++)
        {
            int  x,y;LL  c;scanf("%d%d%lld",&x,&y,&c);
            ins(x,y,c);
        }
        for(int  i=1;i<=n;i++)d[i]=LL(99999999999999999);
        d[st]=0;change(st,0);
        for(int  i=1;i<n;i++)
        {
            int  id=top();
            pop();
            for(int  k=last[id];k;k=a[k].next)
            {
                if(d[a[k].y]>d[id]+a[k].c)
                {
                    d[a[k].y]=d[id]+a[k].c;
                    change(a[k].y,d[a[k].y]);
                }
            }
        }
        for(int  i=1;i<n;i++)printf("%lld ",d[i]);
        printf("%lld
    ",d[n]);
        return  0;
    }
    
  • 相关阅读:
    浅谈VUE中的深拷贝与浅拷贝Object.assign()方法
    echarts详细
    实现HTML调用打开本地软件文件
    el-select 中放置el-tree
    关于前端平时需要用到的方法(未完成)
    10个Vue开发技巧助力成为更好的工程师
    element ui 弹出组件的遮罩层以及多层遮罩解决办法。。
    vue样式穿透 v-deep与deep的具体使用
    # Ansible安装指南
    Linux笔记—网络管理
  • 原文地址:https://www.cnblogs.com/zhangjianjunab/p/11050804.html
Copyright © 2020-2023  润新知