zoukankan      html  css  js  c++  java
  • CF786B Legacy 线段树优化建图

    题目链接

    读题发现,题目里有三种连边方式:

    1.两点之间连一条有向边。

    2.一个点与给定区间中的点连一条有向边。

    3.给定区间中的点和一个点连一条有向边。

    剩下的就是跑最短路。

    两点之间连边很好处理,但是如何在区间之间连边?按平时链式前向星加边,最坏可能是n方的复杂度,建个边都要死。

    这时就有线段树来优化一波。看到区间,还是可以往线段树的方向上去想一想。因为线段树的每个点都代表一段连续的区间。

    可以建两棵线段树,一棵维护区间向外边的点连边,另一棵维护外边的点与区间中连边。用动态开点,保存左儿子和右儿子。

    递归到叶子节点时,叶子节点的编号就是该点。所以一开始的根节点是n。最后回溯时将每个点与左右儿子都连上一条边权为0的边,这样连上某个线段树上的点就等同于某个点与区间中的点都连了一条边,时间复杂度nlog(n+m)(m次询问)。

    按询问连边时就是线段树update操作改一下,最后跑最短路即可,注意要开long long

    代码如下:

    //思路是真的非常巧妙!! 
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e6+7;
    const long long INF=0x3f3f3f3f3f3f3f3f;
    struct node{
        int nxt,to,val;
    }edge[maxn*20];
    priority_queue< pair<long long,int> >q; 
    int head[maxn],cnt;
    bool vis[maxn];
    int n,m,s;
    int opt,u,v,w;
    void add(int x,int y,int v){
        edge[++cnt].nxt=head[x];
        edge[cnt].to=y;
        edge[cnt].val=v;
        head[x]=cnt;
    }
    int num,rt1,rt2;
    int lc[maxn],rc[maxn];
    void build1(int &rt,int l,int r){
        if(l==r){
            rt=l;
            return;
        }
        rt=++num;
        int mid=(l+r)>>1;
        build1(lc[rt],l,mid);
        build1(rc[rt],mid+1,r);
        add(rt,lc[rt],0);
        add(rt,rc[rt],0);
    }
    void build2(int &rt,int l,int r){
        if(l==r){
            rt=l;
            return;
        }
        rt=++num;
        int mid=(l+r)>>1;
        build2(lc[rt],l,mid);
        build2(rc[rt],mid+1,r);
        add(lc[rt],rt,0);
        add(rc[rt],rt,0);
    }
    int l1,r1;
    void update(int rt,int l,int r,int u,int v,int which){
        if(l1<=l&&r<=r1){
            which==2?add(u,rt,v):add(rt,u,v);//包含了当前区间,直接连边。 
            return;
        } 
        int mid=(l+r)>>1;
        if(l1<=mid) update(lc[rt],l,mid,u,v,which);
        if(r1>mid) update(rc[rt],mid+1,r,u,v,which); 
    }
    long long dis[maxn];
    void dijkstra(int x){
        for(int i=1;i<=num;i++) dis[i]=INF;
        memset(vis,false,sizeof(vis));
        q.push(make_pair(0,x));
        dis[x]=0;
        while(q.size()!=0){
            int u=q.top().second;
            q.pop();
            if(vis[u]) continue;
            vis[u]=true;
            for(int i=head[u];i;i=edge[i].nxt){
                int v=edge[i].to;
                if(dis[v]>dis[u]+edge[i].val){
                    dis[v]=dis[u]+edge[i].val;
                    q.push(make_pair(-dis[v],v));
                }
            }
        }
    }
    int main(){
        scanf("%d%d%d",&n,&m,&s);
        num=n;
        build1(rt1,1,n);
        build2(rt2,1,n);
        for(int i=1;i<=m;i++){
            scanf("%d",&opt);
            if(opt==1){
                scanf("%d%d%d",&u,&v,&w);
                add(u,v,w);
            }
            else{
                scanf("%d%d%d%d",&u,&l1,&r1,&w);
                update(opt==2?rt1:rt2,1,n,u,w,opt);
            }
        }
        dijkstra(s);
        for(int i=1;i<=n;i++){
            printf("%lld ",dis[i]<INF?dis[i]:-1);
        }
        return 0;
    }
    View Code

     

  • 相关阅读:
    C#规范整理·异常与自定义异常
    C#规范整理·资源管理和序列化
    C#规范整理·泛型委托事件
    C#规范整理·集合和Linq
    <抽象工厂>比<工厂方法>多了啥(区别)
    <工厂方法>比<简单工厂>多了啥(区别)
    Unable to start Ocelot because either a ReRoute or GlobalConfiguration
    MySQL服务安装
    mysql登录报错“Access denied for user 'root'@'localhost' (using password: YES”)的处理方法
    使用博客系统发生_STORAGE_WRITE_ERROR_错误
  • 原文地址:https://www.cnblogs.com/LJB666/p/11389036.html
Copyright © 2011-2022 走看看