zoukankan      html  css  js  c++  java
  • 2021浙江省大学生程序设计竞赛D题 Shortest Path Query(最短路+思维)

    这道题我们肯定敏锐的观察到题目的一个特殊性质

    只有一个数是另一个数的二进制前缀才能连边,这个性质肯定要用到,等会我们来思考。

    首先是暴力算法,假设要知道两两点的最短路,最暴力的想法就是求floyd,这个太夸张了,我们显然可以放弃掉。

    之后就是对于每个点求迪杰斯特拉,这个算法做一次复杂度过关,但是做多次在普通的图上显然是超时算法。

    这下就用到了题目给的性质。对于一些点,不管他们是什么,如果他们的前缀相同,那么他们就有一些共同的父亲节点,假设可以到达的话。

    我们没有办法存在每个点到任意点的距离,但是我们可以存他们到父亲点的距离,因为前缀没几个。对于这个特殊的图,两点之间可达,一定是有前缀相同

    因此我们可以枚举这个前缀,按照lca的思路,分别是他们到前缀的路径和的最小值。

    这样只要对每个点跑迪杰斯特拉求这些值就行了。

    对每个点跑迪杰斯特拉的正确性在于,我们每次只更新的小的点往大的点的这些路径。对于每个点,他的扩展有01两种情况,因此均摊下来,对于每个以他为根的子图当中的点差不多是log级别,可以接受。

    但是注意每次不能把所有信息的清空,需要遇到一个清空一个。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<ll,int> pll;
    const int N=2e5+10;
    const int M=2e6+10;
    int n,m;
    int h[N],ne[M],e[M],w[M],idx;
    ll dis[N][20];
    int st[N],vis[N];
    ll d[N];
    void add(int a,int b,int c){
        e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
    }
    int cal(int u,int v){
        int cnt=0;
        while(v>u){
            v>>=1;
            cnt++;
        }
        return cnt;
    }
    void dij(int rt){
        priority_queue<pll,vector<pll>,greater<pll>> q;
        q.push({0,rt});
        d[rt]=0;
        vis[rt]=rt;
        while(q.size()){
            auto t=q.top();
            q.pop();
            if(st[t.second]==rt)
                continue;
            st[t.second]=rt;
            dis[t.second][cal(rt,t.second)]=d[t.second];
            for(int i=h[t.second];i!=-1;i=ne[i]){
                int j=e[i];
                if(j<rt)
                    continue;
                if(vis[j]!=rt){
                    vis[j]=rt;
                    d[j]=1e16;
                }
                if(d[j]>d[t.second]+w[i]){
                    d[j]=d[t.second]+w[i];
                    q.push({d[j],j});
                }
            }
        }
    }
    int lca(int a,int b){
        while(a!=b){
            if(a>b)
                a>>=1;
            else
                b>>=1;
        }
        return min(a,b);
    }
    int getsz(int u){
        int cnt=0;
        while(u){
            cnt++;
            u>>=1;
        }
        return cnt;
    }
    int main(){
        ios::sync_with_stdio(false);
        memset(h,-1,sizeof h);
        cin>>n>>m;
        int i,j;
        for(i=1;i<=n;i++){
            for(j=0;j<=19;j++){
                dis[i][j]=1e16;
            }
        }
        for(i=1;i<=n;i++){
            dis[i][0]=0;
        }
        for(i=1;i<=m;i++){
            int a,b,c;
            cin>>a>>b>>c;
            add(a,b,c);
            add(b,a,c);
        }
        for(i=1;i<=n;i++){
            dij(i);
        }
        int q;
        cin>>q;
        while(q--){
            int a,b;
            cin>>a>>b;
            int p=lca(a,b);
            int l1=getsz(a)-getsz(p);
            int l2=getsz(b)-getsz(p);
            ll ans=1e16;
            while(p){
                ans=min(ans,dis[a][l1]+dis[b][l2]);
                l1++,l2++;
                p>>=1;
            }
            if(ans==1e16)
                cout<<-1<<endl;
            else
                cout<<ans<<endl;
        }
        return 0;
    }
    View Code
    没有人不辛苦,只有人不喊疼
  • 相关阅读:
    MySQL 修改数据
    Scala 简介
    tensorflow mnist read_data_sets fails
    Mac安装Homebrew
    MySQL 创建数据表
    MySQL 数据类型
    wc--Linux
    xxx is not in the sudoers file.This incident will be reported.的解决方法
    linux centos下安装dokuwiki
    CentOS修改系统时间
  • 原文地址:https://www.cnblogs.com/ctyakwf/p/14731099.html
Copyright © 2011-2022 走看看