• CF786B 线段树优化建图


    题目大意

    \(n\)个星球,编号\(1-n\),可以建立下面三种边(q次):

    • 星球\(u\)到编号\(l-r\)间的星球每一个建立一条边
    • 编号\(l-r\)间的每一个星球到星球\(u\)建立一条边
    • 星球\(u\)到星球\(v\)建立一条边
      给定起点,问起点到其他个点的最短距离。
      \(n,q \leq 1e5\)

    求最短路并不难,问题在于边数过多。
    由于建立的边都是从某个点到一定的区间范围内的点,可以考虑给区间家里一个点,这样就减少了边的数量。这个就是线段树优化建边。
    方法:

    • 给区间\(1-n\)建立两颗线段树。一棵树叫出树,另一棵叫入树。出树内建立从父节点到左右子节点的有向边。入树内建立从左右孩子到父节点的有向边。
    • 用无向边把出树和入树的叶节点按编号对应连起来。
    • 剩余要建的边;(1)如果是单点相连,直接对入树中对应的点到出树中对应的点建边(2)如果是区间建边,则应用线段树把区间分解到对应的线段树中的点,从入树到出树建边。

    有些代码中把入树和出树的叶子节点设为同一点,这样就不用建立无向边了。
    注意建图后图中的点数和边数!!!


    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e5+10;
    int n,qq,s;
    int cnt,lc[maxn*3],rc[maxn*3];
    int rootin,rootout;
    
    struct edge
    {
    	int u,v,w,nxt;
    }e[maxn*20];
    int head[maxn*3],js;
    void addage(int u,int v,int w)
    {
    	e[++js].v=v;e[js].u=u;e[js].w=w;
    	e[js].nxt=head[u];head[u]=js;
    }
    
    void buildin(int &cur,int l,int r)
    {
    	if(l==r)
    	{
    		cur=l;
    		return ;
    	}
    	cur=++cnt;
    	int mid=(l+r)>>1;
    	buildin(lc[cur],l,mid);
    	buildin(rc[cur],mid+1,r);
    	addage(lc[cur],cur,0);
    	addage(rc[cur],cur,0);
    }
    void buildout(int &cur,int l,int r)
    {
    	if(l==r)
    	{
    		cur=l;
    		return ;
    	}
    	cur=++cnt;
    	int mid=(l+r)>>1;
    	buildout(lc[cur],l,mid);
    	buildout(rc[cur],mid+1,r);
    	addage(cur,lc[cur],0);
    	addage(cur,rc[cur],0);
    }
    void updat(int root,int l,int r,int ql,int qr,int u,int w,int opt)
    {
    	if(ql<=l&&r<=qr)
    	{
    		if(opt==2)addage(u,root,w);
    		else addage(root,u,w);
    		return ;
    	}
    	int mid=(l+r)>>1;
    	if(ql<=mid)updat(lc[root],l,mid,ql,qr,u,w,opt);
    	if(mid<qr)updat(rc[root],mid+1,r,ql,qr,u,w,opt); 
    }
    long long dis[maxn*3];
    queue<int>q;
    bool inq[maxn*3]; 
    void spfa(int s)
    {
    	memset(dis,0x3f,sizeof dis);
    	q.push(s);inq[s]=1;dis[s]=0;
    	while(!q.empty())
    	{
    		int u=q.front();
    		q.pop();inq[u]=0;
    		for(int i=head[u];i;i=e[i].nxt)
    		{
    			int v=e[i].v;
    			if(dis[v]>dis[u]+e[i].w)
    			{
    				dis[v]=dis[u]+e[i].w;
    				if(!inq[v])
    				{
    					q.push(v);
    					inq[v]=1;
    				}
    			}
    		}
    	}
    }
    int main()
    {
    	scanf("%d%d%d",&n,&qq,&s);
    	cnt=n;
    	buildin(rootin,1,n);
    	buildout(rootout,1,n);
    	while(qq--)
    	{
    		int opt,u,l,r,w;
    		scanf("%d",&opt);
    		if(opt==1)
    		{
    			scanf("%d%d%d",&u,&l,&w);
    			addage(u,l,w);
    		}
    		else 
    		{
    			scanf("%d%d%d%d",&u,&l,&r,&w);
    			updat(opt==2?rootout:rootin,1,n,l,r,u,w,opt);
    		}
    	}
    	spfa(s);
    	for(int i=1;i<=n;++i)printf("%lld ",dis[i]==0x3f3f3f3f3f3f3f3f?-1:dis[i]);
    	return 0;
    }
    
  • 相关阅读:
    获取腾讯soso地图坐标代码
    PHP获取服务器的mac地址类
    关于PHPExcel导出Excel时身份证,数字会导出为科学计数的处理方法
    PhpExcel笔记,phpExcel中文帮助手册
    微信开发之——Php批量生成带参数的二维码
    [转载]数据管理——数据血缘关系概述
    HDFS学习总结
    CDH5.7Hadoop集群搭建(离线版)
    QlikSense系列(1)——整体介绍
    Python学习小计
  • 原文地址:https://www.cnblogs.com/gryzy/p/16265898.html
Copyright © 2020-2023  润新知