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

线段树优化建图

时间:2023-11-24 21:22:23浏览次数:39  
标签:lc int 线段 back 建图 build push 优化 id

CF786B

题意:

定义 \((u,v,w)\) 表示 \(u\) 向 \(v\) 连了边权为 \(w\) 的边。

有三种连边操作

  1. \((u,v,w)\)
  2. \(\forall i\in [l,r],(u,i,w)\)
  3. \(\forall i\in [l,r],(i,u,w)\)

求最短路。

暴力加边是 \(O(nm)\) 的,考虑优化。

可以把图建到线段树上,线段树每个结点向左右儿子连 \(w=0\) 的边。那么对于 \(2\) 操作,只需要叶子结点 \(u\) 向对应区间连边即可,一次连边是 \(O(\log n)\) 的。

如下图:

假设现在 \(1\) 结点 向区间 \([2,4]\) 连 \(w=3\) 的边:

下面考虑 \(3\) 操作,只需要再建一棵线段树,每个结点向其父亲连 \(w=0\) 的边即可。

假设现在 \([1,3]\) 区间向 \(2\) 结点连 \(w=4\) 的边:

注意两棵树的叶子节点本质是同一个点,所以两点间也要连 \(w=0\) 的双向边。

那么这道题就解决了,总边数 \(O(8n+m\log n)\),跑最短路即可。

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5,K=4e5;
int n,m,s,id[N],vis[N*8];
long long d[N*8];
vector<pair<int,int>> G[N*8];

#define lc (k<<1)
#define rc (k<<1|1)
#define mid (l+r>>1)
void build(int k=1,int l=1,int r=n)
{
    if(l==r) {id[l]=k;G[k].push_back({k+K,0}),G[k+K].push_back({k,0});return;}
    G[k].push_back({lc,0}),G[k].push_back({rc,0});
    G[lc+K].push_back({k+K,0}),G[rc+K].push_back({k+K,0});
    build(lc,l,mid),build(rc,mid+1,r);
}
void upd(int x,int y,int u,int w,int op,int k=1,int l=1,int r=n)
{
    if(l>=x&&r<=y)
    {
        if(!op) G[u].push_back({k,w});
        else G[k+K].push_back({u,w});
        return;
    }
    if(x<=mid) upd(x,y,u,w,op,lc,l,mid);
    if(mid<y) upd(x,y,u,w,op,rc,mid+1,r);
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>m>>s;
    build();
    while(m--)
    {
        int op,u,v,l,r,w;cin>>op>>u;
        if(op==1) {cin>>v>>w;G[id[u]].push_back({id[v],w});}
        else {cin>>l>>r>>w;upd(l,r,id[u],w,op&1);}
    }
    memset(d,0x3f,sizeof(d));
    priority_queue<pair<long long,int>> q;
    q.push({0,id[s]});d[id[s]]=0;
    while(!q.empty())
    {
        int u=q.top().second;q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(auto [v,w]:G[u]) if(d[u]+w<d[v]) d[v]=d[u]+w,q.push({-d[v],v});
    }
    for(int i=1;i<=n;i++) if(d[id[i]]>1e18) cout<<-1<<' ';else cout<<d[id[i]]<<' ';
}

P5025

优化建图 + 强连通分量。

注意 DAG 上不能 dp 点权和,改为对每个强连通分量求能引爆的区间。

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=2e6+5,P=1e9+7;
int n,ans,tot,id[N],rev[N],vis[N],L[N],R[N],x[N],y[N],l[N],r[N];
vector<int> G[N],g[N];

#define lc (k<<1)
#define rc (k<<1|1)
#define mid (l+r>>1)
void build(int k=1,int l=1,int r=n)
{
    tot=max(tot,k);
    if(l==r) {id[l]=k,rev[k]=l;return;}
    G[k].push_back(lc),G[k].push_back(rc);
    build(lc,l,mid),build(rc,mid+1,r);
}
void upd(int x,int y,int u,int k=1,int l=1,int r=n)
{
    if(l>=x&&r<=y) {if(id[u]!=k) G[id[u]].push_back(k);return;}
    if(x<=mid) upd(x,y,u,lc,l,mid);
    if(mid<y) upd(x,y,u,rc,mid+1,r);
}

inline int rd()
{
    int x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^48);
    return x*f;
}

int dn,top,cnt,stk[N],dfn[N],low[N],bel[N];
void tarjan(int u)
{
    stk[++top]=u;
    dfn[u]=low[u]=++dn;
    for(int v:G[u])
    {
        if(!dfn[v]) tarjan(v),low[u]=min(low[u],low[v]);
        else if(!bel[v]) low[u]=min(low[u],dfn[v]); 
    }
    if(low[u]==dfn[u])
    {
        int v;++cnt;
        do{
            v=stk[top--];
            bel[v]=cnt;
            if(rev[v]) l[cnt]=min(l[cnt],L[rev[v]]),r[cnt]=max(r[cnt],R[rev[v]]);
        }while(v!=u);
    }
}

void dfs(int u)
{
    vis[u]=1;
    for(int v:g[u])
    {
        if(!vis[v]) dfs(v);
        l[u]=min(l[u],l[v]);
        r[u]=max(r[u],r[v]);
    }
}

signed main()
{
    n=rd();build();
    memset(l,0x3f,sizeof(l));
    for(int i=1;i<=n;i++) x[i]=rd(),y[i]=rd();
    for(int i=1;i<=n;i++)
    {
        L[i]=lower_bound(x+1,x+1+n,x[i]-y[i])-x;
        R[i]=upper_bound(x+1,x+1+n,x[i]+y[i])-x-1;
        upd(L[i],R[i],i);
    }
    tarjan(1);
    for(int i=1;i<=tot;i++) for(int j:G[i]) if(bel[i]!=bel[j]) g[bel[i]].push_back(bel[j]);
    for(int i=1;i<=cnt;i++) if(!vis[i]) dfs(i);
    for(int i=1;i<=n;i++) ans=(ans+1ll*i*(r[bel[id[i]]]-l[bel[id[i]]]+1))%P;
    cout<<ans<<'\n';
}

标签:lc,int,线段,back,建图,build,push,优化,id
From: https://www.cnblogs.com/spider-oyster/p/17854797.html

相关文章

  • 【刷题记录】20231124 线段树分治
    做题记录:注意到每次相当于\(0\)后面加\(1\),\(1\)后面加\(0\),因此每次可以合并01和10然后将问题规模减半。黑白染色,白格子=lcm+1,黑格子=prime相乘。发现横着竖着有六个质数,斜着只用四个质数。调整一下顺序即可。状压DP。考虑S作为前缀max时S与U-S的排列方案数。S每......
  • 优化 AWS 云成本:降本增效的实用指南
    引言AWS云提供了广泛的服务,但有效地管理成本对于企业至关重要。通过实施一系列的最佳实践,你可以降低成本,提高效率,同时保持灵活性。本文将重点介绍如何实施和监控AWS云成本的优化措施。实施阶段1. 选择适当的实例类型和规格首先,仔细评估你的工作负载需求,并选择最适合的实例类型......
  • DP优化技巧
    感谢https://www.luogu.com.cn/user/249973#main老师。DP优化技巧矩阵优化DP1.矩阵快速幂(优化dp)2.四边形不等式优化dp(a,b,c,d)(ac+bd<=ad+bc)3.数据结构优化dp(线段树)4.单调队列、二分栈优化dp5.斜率优化dp矩阵定义矩阵乘法(更常见),矩阵加法矩阵加法一般式:\(C_{i,j}=A_{i,......
  • 后端的性能优化有哪些方面?
    Java的性能优化可以从多个方面入手,从影响性能的方面考虑一下。包括以下几个方面:线程池调优:适当地调整线程池的大小和线程数,可以提高程序的并发性能和响应速度。内存管理:合理地管理内存使用,包括对象的创建和销毁,可以提高程序的执行效率。IO操作优化:采用NIO方式可以减少IO......
  • blender布线优化
    在雕刻模式下选中滑动松弛(拓补)笔刷进行使用:该笔刷将网格的拓扑滑移到细节更多的区域,同时尽可能少地改变网格的集合形状。当按下Shift键时,该笔刷进入平滑模式该模式下笔刷将在不改变网格的体积的情况下使四边面的分布更平均。编辑模式下按A全选点然后按M选择按距离合并......
  • mysql 一些优化参数
     大批量数据加载优化load数据加载格式:loaddatalocalinfile'文件路径'intotable表名fieldsterminatedby'[分隔符]'lineterminatedby'[换行符]'11、首先,检测全局变量‘local_infile’的状态,如果是off状态则是不可用showglobalvariableslike'local_infile';......
  • Centos系统udp丢包&内核参数优化
    echo0>/proc/irq/31/smp_affinity_listecho1>/proc/irq/33/smp_affinity_list这两个命令是用于设置Linux中中断处理程序的亲和性,以提高系统的性能和稳定性。在Linux系统中,系统中断(IRQ)是由硬件触发的,它们通常被用于处理来自硬件设备的请求(例如,网络接口卡、磁盘控制器......
  • 交点 - 求两线段交点2
    效果 会用到的知识相交-两线段是否相交-yanghui01-博客园(cnblogs.com)线性代数-已知点求直线方程-yanghui01-博客园(cnblogs.com)交点-两直线交点-yanghui01-博客园(cnblogs.com) //两线段是否相交publicstaticboolIsTwoSegmentIntersect2(V......
  • 「线段树」笔记
    基础建树voidbuild(intp,intl,intr){ t[p]=(tree){l,r,0}; if(l==r) { t[p].sum=val[l]; return; } intmid=(l+r)>>1; build(lp,l,mid); build(rp,mid+1,r); pushup(p);}单点修改(和)voidupdate(intp,intx,intk){ if(t[......
  • 【模板】可持久化线段树 2
    【模板】可持久化线段树2题目背景这是个非常经典的可持久化权值线段树入门题——静态区间第$k$小。数据已经过加强,请使用可持久化权值线段树。同时请注意常数优化。题目描述如题,给定$n$个整数构成的序列$a$,将对于指定的闭区间$[l,r]$查询其区间内的第$k$小值。输......