CodeForces 787D------线段树建图

本文介绍了一种将线段树与Dijkstra算法结合的创新方法,通过构建两个线段树并利用它们之间的连接来解决复杂的问题。这种方法首先创建两个方向相反的线段树,然后在这两个树之间建立联系,最后使用Dijkstra算法在生成的图上寻找最短路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

看到了大佬的做法,才发现竟然可以这样写,看起来挺复杂的,其实是个线段树建图,再用Dijkstra的过程
大佬链接
建立一个线段树a,这个线段树使所有的父节点都指着子节点(单向),权值为0。
再建立一个线段树b,这个线段树使所有的子节点都指着子父点,权值为0。
并且,a树的每个子节点都指着b树相对的子节点,且权值依然为0。
第一个是v到u,那么就是b树的v节点指向b树的u节点。
第二个是v到,L到R每一个。那么就是b树的节点v,存一条路径,可以到a树的L到R区间。(点到区间)
第三个是L到R每一个,到v。那么就是b树的L到R区间,指向a树的v节点。(区间到点)
记住,这里的所有特征就是都是从b树(向上指的那棵树出发)
最后再用所得到的图,进行Dijkstra算法。

#include<bits/stdc++.h>
#define ll long long
#define pli pair<ll,int>
#define lson k<<1,l,mid
#define rson k<<1|1,mid+1,r
using namespace std;
const int MX=1e5+9;
const ll inf=1e10+9;
int n,m,st,cnt=0,pa[MX],pb[MX],vis[MX<<3];
ll dis[MX<<3];

struct node{
    int l,r,num;
}a[MX<<2],b[MX<<2];

struct Node{
    int to;
    ll w;
};

vector<Node> vec[MX<<3];

void builda(int k,int l,int r){
    a[k].l=l,a[k].r=r,a[k].num=++cnt;
    if( l==r ){
        pa[l]=a[k].num;
        vec[pa[l]].push_back({pb[l],0});
        return ;
    }
    int mid=(l+r)>>1;
    builda(lson);
    builda(rson);
    vec[a[k].num].push_back({a[k<<1].num,0});
    vec[a[k].num].push_back({a[k<<1|1].num,0});
}

void buildb(int k,int l,int r){
    b[k].l=l,b[k].r=r,b[k].num=++cnt;
    if( l==r ){
        pb[l]=b[k].num;
        return ;
    }
    int mid=(l+r)>>1;
    buildb(lson);
    buildb(rson);
    vec[b[k<<1].num].push_back({b[k].num,0});
    vec[b[k<<1|1].num].push_back({b[k].num,0});
}

void updatea(int k,int l,int r,int L,int R,int num,ll w){
    if( L<=l && r<=R ){
        vec[num].push_back({a[k].num,w});
        return ;
    }
    int mid=(l+r)>>1;
    if( L<=mid )
        updatea(lson,L,R,num,w);
    if( mid<R )
        updatea(rson,L,R,num,w);
}

void updateb(int k,int l,int r,int L,int R,int num,ll w){
    if( L<=l && r<=R ){
        vec[b[k].num].push_back({num,w});
        return ;
    }
    int mid=(l+r)>>1;
    if( L<=mid )
        updateb(lson,L,R,num,w);
    if( mid<R )
        updateb(rson,L,R,num,w);
}

void dij(){
    memset(vis,0,sizeof(vis));
    priority_queue<pli,vector<pli>,greater<pli> > que;
    for( int i=1 ; i<=cnt ; i++ )
        dis[i]=inf;
    dis[pa[st]]=0;
    que.push({0,pa[st]});
    while( !que.empty() ){
        ll w=que.top().first;
        int u=que.top().second;
        que.pop();
        vis[u]=1;
        for( int i=0 ; i<vec[u].size() ; i++ ){
            int v=vec[u][i].to;
            ll val=vec[u][i].w;
            if( vis[v]==1 )
                continue;
            if( dis[v]>dis[u]+val ){
                dis[v]=dis[u]+val;
                que.push({dis[v],v});
            }
        }
    }
    for( int i=1 ; i<=n ; i++ ){
        ll ans=dis[pa[i]]==inf?-1:dis[pa[i]];
        printf("%lld%c",ans,i==n?'\n':' ');
    }
    return ;
}

int main()
{
    freopen("input.txt","r",stdin);
    scanf("%d %d %d",&n,&m,&st);
    buildb(1,1,n);
    builda(1,1,n);
    while( m-- ){
        int order,u,v,l,r;
        ll w;
        scanf("%d",&order);
        if( order==1 ){
            scanf("%d %d %lld",&u,&v,&w);
            vec[pb[u]].push_back({pa[v],w});  // b节点到a节点
        }
        else if( order==2 ){
            scanf("%d %d %d %lld",&v,&l,&r,&w);
            updatea(1,1,n,l,r,pb[v],w);  // b节点到a的lr范围
        }
        else{
            scanf("%d %d %d %lld",&v,&l,&r,&w);
            updateb(1,1,n,l,r,pa[v],w);  // b的lr范围到a节点
        }
    }
    dij();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值