首页 > 其他分享 >Legacy (线段树优化建图)

Legacy (线段树优化建图)

时间:2024-01-25 19:55:35浏览次数:26  
标签:int lim 线段 add 建图 Legacy mx lx dis

题目链接:Legacy - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题解:

考虑题目中一个点向区间连边,如真的对区间中的每一点分别连边后跑最短路,时间空间都要炸。

因为是一个点向区间连边,考虑线段树。

1到n构造两颗区间线段数

 观察上图(从网上搬的)

两颗线段树,一颗入树父亲向儿子连边,用来优化指向叶子的边(左边的树)一颗出树儿子向父亲连边用来优化从叶子指出的点(右边的树)两棵树完全相同。

每个叶子为表示一个星球(可以理解为拆点处理入边和出边),对应的星球之间连一条边,边权为0,因为一个点到自己不花费代价(图中黄色的边)

对于题中给出的操作,操作一可以与操作二等价(也可以与操作三等价)理解为一个点可以去一个区间。

操作二从第二个树上的叶子v向第一颗树上对应的区间连边(与线段树操作相同)

操作三从第一个树上的叶子v向第二颗颗树上对应的区间连边(与上图不太相同,可以自己画图理解)

最后从s点跑最短路

#include <bits/stdc++.h>
using namespace std;
const int N=5e5+10;
const int M=4e6+10;
#define int long long//数据比较大,会爆int
int tot,n,q,s,cnt,root,nxt[M],go[M],hd[M],dx[M],vis[M],dis[M],jz[M],ans,mi,mx;
void add(int x,int y,int w)
{
    nxt[++tot]=hd[x],go[tot]=y,jz[tot]=w,hd[x]=tot;
    return ;
}
void build(int x,int l,int r,int lx,int lim)//用lim将第一颗树和第二棵树一起处理
{
    if(l==r)
    {
        x=x+lim;
        if(x>mx)mx=x;
        dx[++cnt]=x;
        return ;
    }
    int mid=(l+r)>>1;
    if(lx==1)add(x+lim,x*2+lim,0),add(x+lim,x*2+1+lim,0);
    if(lx==2)add(x*2+lim,x+lim,0),add(x*2+1+lim,x+lim,0);
    build(x*2,l,mid,lx,lim);
    build(x*2+1,mid+1,r,lx,lim);
}
void search(int x,int l,int r,int lt,int rt,int v,int w,int lx)
{
    if(lt<=l&&rt>=r)
    {
        if(lx==1)add(v,x,w);
        else add(x+root-1,v,w);
        return ;
    }
    int mid=(l+r)>>1;
    if(lt<=mid)search(x*2,l,mid,lt,rt,v,w,lx);
    if(rt>mid)search(x*2+1,mid+1,r,lt,rt,v,w,lx);
    return ;
}
struct node
{
    int id,zhi;
    bool operator<(const node& a)const {return zhi>a.zhi;}
    node(int x,int y){id=x,zhi=y;}
};
void dij(int x)
{
    memset(dis,0x7f,sizeof(dis));
    priority_queue<node> q;
    dis[x]=0;
    q.push(node(x,0));
    while(!q.empty())
    {
        int u=q.top().id;q.pop();
        if(vis[u])continue;
        vis[u]=1;
        for(int i=hd[u];i;i=nxt[i])
        {
            int v=go[i];
            if(dis[v]>dis[u]+jz[i])
            {
                dis[v]=dis[u]+jz[i];
                if(!vis[v])q.push(node(v,dis[v]));
            }
        }
    }
    return ; 
}
signed main()
{
    scanf("%lld%lld%lld",&n,&q,&s);
    build(1,1,n,1,0);
    root=mx+1;//第二颗树的root
    build(1,1,n,2,mx);//从第二棵树的点表示为mx+1,mx+2,mx+3而不是mx,2*mx,2*mx+1节约空间不然会MLE。
    mx=0,mi=1e9;
    for(int i=1;i*2<=cnt;i++)//dx存叶子,考虑线段树过程以及两颗线段树相同,可以得出第i(i<=cnt/2)或i-cnt/2(i>cnt/2)个的对应线段树节点为i,
    {
        if(dx[i]>mx)mx=dx[i];
        if(dx[i]<mi)mi=dx[i];
        add(dx[i],dx[i+cnt/2],0);
        add(dx[i+cnt/2],dx[i],0);//黄色边,即自己向自己连边
    }    
    for(int i=1;i<=q;i++)
    {
        int lx,l,r,v,w;;scanf("%lld",&lx);
        if(lx==1)
        {
            scanf("%lld%lld%lld",&v,&l,&w);r=l;
            search(1,1,n,l,r,dx[v+cnt/2],w,1);
        }
        if(lx==2)
        {
            scanf("%lld%lld%lld%lld",&v,&l,&r,&w);
            search(1,1,n,l,r,dx[v+cnt/2],w,1);            
        }
        if(lx==3)
        {
            scanf("%lld%lld%lld%lld",&v,&l,&r,&w);
            search(1,1,n,l,r,dx[v],w,2);                
        }
    }
    dij(dx[s]);//最短路
    for(int i=1;i<=n;i++)
    {
        if(dis[dx[i]]==9187201950435737471) dis[dx[i]]=-1;//特殊处理不连通
        printf("%lld ",dis[dx[i]]);dis存的是线段树节点
    }
    return 0;
}

别人的建图

void build(int p,int l,int r){
    if(l==r){a[l]=p;return ;}
    int mid=(l+r)/2;
    add(p,p<<1,0),add(p,p<<1|1,0);
    add((p<<1)+K,p+K,0),add((p<<1|1)+K,p+K,0);
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r); 
}

提前计算第一颗树的最大值,计为K.

对于i存i的对应线段树节点。

其他差别不大

标签:int,lim,线段,add,建图,Legacy,mx,lx,dis
From: https://www.cnblogs.com/storms11/p/17988040

相关文章

  • P3374 【模板】树状数组 1(线段树)
    【模板】树状数组1题目描述如题,已知一个数列,你需要进行下面两种操作:将某一个数加上x求出某区间每一个数的和输入格式第一行包含两个正整数n,m,分别表示该数列数字的个数和操作的总个数。第二行包含n个用空格分隔的整数,其中第i个数字表示数列第i项的初始值......
  • 线段树合并
    线段树合并线段树合并可以使很多跑不过的暴力,特别是树上暴力的时间复杂度正确,与树分治的区别在于,线段树合并必须依次处理节点,但优势在于,保持了树的形态。算法思路引入CF600ELomsatgelral使用一个数组记录该子树内的颜色出现次数。每次每个节点暴力将儿子的信息合并到自己......
  • Ybt 金牌导航 6.1.H. 时空旅行 / P5416 [CTSC2016] 时空旅行(线段树分治+凸包)
    题意简述初始有版本\(0\),其中仅包含点\(0\),且\(c_0\)给出,\(x_0=0\)。对于第\(i\)个版本,它依赖第\(fr_i\)个版本,而且会在父级版本的基础上进行以下两种操作之一:插入一个新点,并且会给出\(x_i\)和\(c_i\)。删除一个本就存在的点(不为\(0\))给出\(m\)次询问,每次给出......
  • 主席树(可持久化线段树)
    主席树前言主席树也是一种数据结构,是线段树的进阶,作用是可以保留历史版本,支持回退,就是回到之前某个未修改的状态。就像我们在写博客时如果误删了重要的内容,就可以用Ctrl+z撤回一次操作,也可以用Ctrl+Shift+z还原我们撤回的操作,这就需要存下每次编辑的操作。基本原理可......
  • 「笔记」李超线段树
    目录写在前面引入线段区间修改单点查询完整代码例题P4254[JSOI2008]BlueMary开公司P3081[USACO13MAR]HillWalkG写在最后写在前面LiChaoTree也是LCT(智将和典中典之楼房重建都是\(O(\log^2n)\)地进行修改的样子,但是楼房重建是拆分完区间后再\(O(\log^2n)\)地向上......
  • 线段树二分
    问题描述维护长度为\(n\)的序列\(a\),支持以下操作:1ix:把\(a_i\)修改为\(x\)。2ix:询问最小的\(j\)满足\(j>=i\)且\(a_j>=x\)。\(1\leqn\leq10^6\)解决方法在线段树外直接二分可以做到\(O(n\log^2n)\)的时间复杂度,加上简单的剪枝可能效率会高一些,但都无......
  • CF-240-F-线段树
    240-F题目大意给定一个长为\(n\)的字符串,由小写字母组成。由\(m\)次操作,每次操作给定一个区间\([l,r]\),要求你把区间中的字符进行重新排列,要求重排后的子串是字典序最小的回文串,如果无法得到回文串则忽略这次操作。输出\(m\)次操作之后的字符串。Solution涉及区间操作,考虑用......
  • CF-242-E-线段树
    242-E题目大意给定一个长为\(n\)的数组\(a\),\(q\)次操作,操作分两种:\(1.l,r\):输出\({\sum_{i=l}^{r}a_i}\)。\(2.l,r,x\):把区间\([l,r]\)中的元素都异或上\(x\)。Solution区间信息具有可并性,线段树能够快速得到所求区间的信息。线段树节点中维护一个数组\(bit\),\(bit[i]\)......
  • 数据结构——线段树 入门以后 学习笔记
    数据结构——线段树入门以后学习笔记入门笔记有时间写。才发现我不会线段树。/ll可以看出来我很喜欢class/cf有的代码需要前置:usingll=longlong;constexprllmod=998244353;constexprintroot=1;P3372线段树1classseg_t{private:structemm{......
  • 学习笔记——线段树
    线段树(SegmentTree)1.建树首先我们要明白线段树中的每个节点都代表一个区间,而对于线段树中的每个内部节点\(\left[l,r\right]\),它的左子节点是\(\left[l,mid\right]\),右子节点是\(\left[mid+1,r\right]\),其中\(mid=(l+r)/2\)(向下取整)。然后我们可以让根节点的编号为\(......