• 「珍贵历史资料鉴赏」CF786B 题解


    写在前面

    偶然翻到一篇 2019-08-07 18:58 写的未发布的题解。
    给大家找点乐子玩。

    正文

    知识点:线段树优化建图

    线段树优化建图
    用于解决
    类似 从 x 向区间[L,R]的
    区间连边问题

    原图:
    1 2 3 4
    向其中添加一系列虚点 ,
    并将虚点与实点 , 虚点与虚点 连边,
    将添加的边,的边权值设为0 之后:
    o
    /
    o o
    / /
    1 2 3 4

    形成一棵满二叉树

    对于这棵满二叉树,
    可以将其当作一棵线段树,
    来进行一系列的操作:

    1. 对于 从 x 向区间[L,R]连边操作:
      由于新添加的边权全为0
      所以只需要将 x 与
      "管辖" 着 [L,R] 区域的虚点连线即可
      可以类比线段树的区间查询,
      进行递归查询,

      要连接的边数,
      由 R-L级别 ,转化为了log(R-L)级别

    2. 对于 从 区间[L,R] 向 x 连边操作:
      逆向操作
      考虑反向连边即可
      同时在初始化时,
      也要注意 初始化反向的边

    3. 对于 从x 向 y连边操作
      直接连接即可

    算法实现:
    先进行两次 初始化建树操作
    分别处理正向与反向的 ,
    与虚点之间的连边,
    将其设置为0.

    然后对于每种连边操作,
    按照上述法则,
    将x与实点/虚点 连边

    最后跑一遍最短路即可

    注意的一些小点:
    1.为了不影响实点之间的连边
    虚点,即线段树上的点 的编号,
    从n+1开始编号

    #include<cstdio>
    #include<queue>
    #include<cstring>
    #define ll long long
    const int MARX = 3e5+10;
    const ll INF = 0x3f3f3f3f3f3f3f3f;
    //============================================
    struct edge
    {
    	int u,v,w,ne;
    }e[MARX<<4];//各边 
    int n,q,start;//输入的初始变量 
    std:: queue <int> qu;//spfa的队列 
    int num,head[MARX];//建图变量 
    int cnt,root1,root2,ls[MARX],rs[MARX];//线段树变量 (ls为左儿子,rs为右儿子) 
    ll dis[MARX];//spfa 变量 
    //============================================
    inline void add(int u,int v,int w)
    {
    	e[++num].v=v,e[num].u=u,e[num].w=w;
    	e[num].ne=head[u],head[u]=num;
    }
    void build1(int &s,int l,int r)//初始化 从父亲向儿子的0边 
    {
    	if(l==r)//递归向下,直到 到达叶子部的 实点, 
    	{
    	  s=l;//实点的编号不变 
    	  return;
    	}
    	s = ++cnt;//更新当前虚点的编号 
    	int mid = (l+r)>>1;
    	build1(ls[s],l,mid);//递归左右子树 
    	build1(rs[s],mid+1,r);
    	add(s,ls[s],0);//向下连0边 
    	add(s,rs[s],0);
    }
    void build2(int &s,int l,int r)//类比build1,进行反向连0边 
    {
    	if(l==r)
    	{
    	  s=l;
    	  return;
    	}
    	s = ++cnt;
    	int mid = (l+r)>>1;
    	build2(ls[s],l,mid);
    	build2(rs[s],mid+1,r);
    	add(ls[s],s,0);
    	add(rs[s],s,0);
    }
    int L,R;//要操作的左右区间 
    void updata(int s,int l,int r,int u,int w,int type)//s:当前点编号; l,r:当前点s管辖区间 ; type:操作的种类 
    {
    	if(L<=l && r<=R)//操作区间 包含 当前区间 
    	{
    	  if(type==2) add(u,s,w);//按照种类,进行连边操作. 
    	  else add(s,u,w);
    	  return ;
    	}
    	int mid=(l+r)>>1;
    	if(L<=mid) updata(ls[s],l,mid,u,w,type);//递归进行操作 
    	if(mid<R) updata(rs[s],mid+1,r,u,w,type);
    }
    void spfa(int start)//他死了 
    {
    	memset(dis,0x3f,sizeof(dis));
    	dis[start]=0;qu.push(start);
    	while(!qu.empty())
    	{
    	  int u=qu.front(); qu.pop();
    	  for(int i=head[u];i;i=e[i].ne)
    	  {
    	  	int v=e[i].v,w=e[i].w;
    	  	if(dis[u]+w < dis[v])
    	  	{
    	  	  dis[v]=dis[u]+w ;
    		  qu.push(v);	
    		}
    	  }
    	}
    }
    //============================================
    signed main()
    {
    	scanf("%d%d%d",&n,&q,&start);
    	cnt=n;//为不影响实点之间的连边 , 虚点,从n+1开始编号 
    	build1(root1,1,n),build2(root2,1,n);//初始化 
    	for(int i=1;i<=q;i++)
    	{
    	  int opt,u,v,w;
    	  scanf("%d",&opt);
    	  if(opt==1)
    	  {
    	  	scanf("%d%d%d",&u,&v,&w);
    	  	add(u,v,w);//直接连接 
    	  }
    	  else
    	  {
    	  	scanf("%d%d%d%d",&u,&L,&R,&w);
    	  	updata((opt==2?root1:root2),1,n,u,w,opt);//按照种类,进行操作 
    	  }
    	}
    	
    	spfa(start);
    	/*
    	for(int i=1;i<=n;i++)
    	  for(int j=head[i];j;j=e[j].ne)
    	    printf("%d %d %d
    ",i,e[j].v,e[j].w);
    	*/
    	for(int i=1;i<=n;i++) printf("%lld ",dis[i]<INF?dis[i]:-1); 
    }
    
  • 相关阅读:
    CTF web之旅 20
    CTF web之旅 19
    CTF web之旅 18
    CTF web之旅 17
    CTF web之旅 16
    php数组遍历函数array_reduce、array_filter()、array_map()、array_walk()三者的用法和区别
    composer require安装的模块依赖和`composer.json`中版本冲突
    Linux执行shell出现错误bad interpreter: No such file or directory解决方法
    dcat-admin
    在Laravel外独立使用Eloquent
  • 原文地址:https://www.cnblogs.com/luckyblock/p/14258281.html
Copyright © 2020-2023  润新知