zoukankan      html  css  js  c++  java
  • [NOI2018]归程

    题目大意:
    一张n个点m条边的无向图,每条边有权值和高度。每次询问给出起点v和一个高度p,你在开始时可以花费0的价值走过高度大于p的边,从第一次走过高度小于等于p的边开始,走过一条边要花费相应的权值。求走到1的最小花费。强制在线。
    解题思路:
    最短路跑Dijkstra即可(SPFA没有了)。
    如果可以离线,则并查集维护最大生成树即可。强制在线的话,也可以可持久化并查集,带两只log。
    正解是Kruskal重构树。
    我们在Kruskal的过程中,每次选中一条边,合并两个连通块,都新建一个节点,然后把这个节点作为两个连通块的父亲,高度设为边的高度,距离则设为两个儿子的距离的最小值。
    如果高度大于这条边(即这个节点),则两边的点都能直接走到,因此这个点的距离设为儿子节点距离的最小值。
    这样构造出来的树有以下性质:
    1. 这是一棵二叉树。
    2. 点从下往上高度递减。
    3. 原来的节点都是树的叶子结点。
    然后对于任意一个询问,直接向上倍增,找到最上面一个高度大于p的,答案就是该节点的距离。
    然后就只带一只log辣,而且代码很好写。

    C++ Code:

    #include<bits/stdc++.h>
    #include<ext/pb_ds/priority_queue.hpp>
    typedef long long LoveLive;
    const int N=200005;
    inline int readint(){
        int c=getchar(),d=0;
        for(;!isdigit(c);c=getchar());
        for(;isdigit(c);c=getchar())
        d=(d<<3)+(d<<1)+(c^'0');
        return d;
    }
    struct edge{
        int to,nxt,dis;
    }e[N<<2];
    struct kruskal_edge{
        int u,v,h;
        inline bool operator<(const kruskal_edge&rhs)const{return h>rhs.h;}
    }ee[N<<1];
    struct heap_node{
        LoveLive d;int u;
        inline bool operator<(const heap_node&rhs)const{return d>rhs.d;}
    };
    __gnu_pbds::priority_queue<heap_node>hp;
    int n,m,head[N],cnt,fa[N<<1][21],nodes,ff[N<<1],H[N<<1];
    LoveLive ans,d[N<<1];
    bool vis[N];
    inline int find(int x){return x==ff[x]?x:ff[x]=find(ff[x]);}
    inline void addedge(int from,int to,int dis){
        e[++cnt]=(edge){to,head[from],dis};
        head[from]=cnt;
        e[++cnt]=(edge){from,head[to],dis};
        head[to]=cnt;
    }
    void dijkstra(){
        memset(d,0x3f,sizeof d);
        d[1]=0;
        hp.push((heap_node){0,1});
        memset(vis,0,sizeof vis);
        while(!hp.empty()){
            heap_node nw=hp.top();
            hp.pop();
            if(vis[nw.u])continue;
            vis[nw.u]=1;
            for(int i=head[nw.u];i;i=e[i].nxt)
            if(!vis[e[i].to]&&d[e[i].to]>d[nw.u]+e[i].dis){
                d[e[i].to]=d[nw.u]+e[i].dis;
                hp.push((heap_node){d[e[i].to],e[i].to});
            }
        }
    }
    int main(){
        for(int T=readint();T--;){
            ans=cnt=0;
            memset(e,0,sizeof e);
            memset(head,0,sizeof head);
            n=readint(),m=readint();
            for(int i=1;i<=m;++i){
                int u=readint(),v=readint(),l=readint(),a=readint();
                addedge(u,v,l);
                ee[i]=(kruskal_edge){u,v,a};
            }
            dijkstra();
            std::sort(ee+1,ee+m+1);
            for(int i=1;i<=n;++i)ff[i]=i,ff[i+n]=i+n;
            nodes=n;
            int less_node=n-1;
            memset(H,0,sizeof H);
            for(int i=1;less_node&&i<=m;++i){
                int x=find(ee[i].u),y=find(ee[i].v);
                if(x!=y){
                    --less_node;
                    ff[x]=ff[y]=fa[x][0]=fa[y][0]=++nodes;
                    H[nodes]=ee[i].h;
                    d[nodes]=std::min(d[x],d[y]);
                }
            }
            for(int j=1;j<21;++j)
            for(int i=1;i<=nodes;++i)
            fa[i][j]=fa[fa[i][j-1]][j-1];
            for(int Q=readint(),K=readint(),S=readint();Q--;){
                int v=(1ll*readint()+K*ans-1)%n+1,p=(1ll*readint()+K*ans)%(S+1);
                for(int j=20;~j;--j)if(H[fa[v][j]]>p)v=fa[v][j];
                printf("%lld
    ",ans=d[v]);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    程序基址,X64Dbg软件常用调试技巧查找系统函数调用位置执行到指定位置断点
    #pragma的常用方法讲解,为什么有了条件编译符号“DEBUG”还要来个Debugger.IsAttached
    JDK17Src0.java.base
    nmon的安装和使用
    64位下的相对指令地址X86指令格式(操作码列和指令列解释)
    内存中的程序剖析
    Linux I/O 原理和 Zerocopy 技术全面揭秘
    Ubuntu命令行的垃圾箱,回收站
    SecureCRT密钥链接阿里云
    HTTP API 认证授权术
  • 原文地址:https://www.cnblogs.com/Mrsrz/p/9345431.html
Copyright © 2011-2022 走看看