zoukankan      html  css  js  c++  java
  • The Shortest Statement CodeForces

    题目描述

    You are given a weighed undirected connected graph, consisting of n vertices and mm edges.

    You should answer q queries, the i-th query is to find the shortest distance between vertices ui and vi.

    Input

    The first line contains two integers n and m (1≤n,m≤105,m−n≤20)

    m — the number of vertices and edges in the graph.

    Next m lines contain the edges: the i-th edge is a triple of integers vi,ui,di (1≤ui,vi≤n,1≤di≤109,ui≠vi)

    This triple means that there is an edge between vertices ui and vivof weight di. It is guaranteed that graph contains no self-loops and multiple edges.

    The next line contains a single integer q (1≤q≤105) — the number of queries.

    Each of the next q lines contains two integers ui and vi (1≤ui,vi≤n)— descriptions of the queries.

    Pay attention to the restriction m−n ≤ 20

    Output

    Print q lines.

    The i-th line should contain the answer to the i-th query — the shortest distance between vertices ui and vi.

    Examples

    Input

    3 3
    1 2 3
    2 3 1
    3 1 5
    3
    1 2
    1 3
    2 3
    

    Output

    3
    4
    1
    

    Input

    8 13
    1 2 4
    2 3 6
    3 4 1
    4 5 12
    5 6 3
    6 7 8
    7 8 7
    1 4 1
    1 8 3
    2 6 9
    2 7 1
    4 6 3
    6 8 2
    8
    1 5
    1 7
    2 3
    2 8
    3 7
    3 4
    6 8
    7 8
    

    Output

    7
    5
    6
    7
    7
    1
    2
    7
    

    分析

    一句话题意:给出一个无向连通图,图中有n个点,m条边,m-n<=20,给出q个询问,每一个询问包含两个整数u和v,对于每一次询问,输出u和v之间的最短路

    善良的出题人特别强调了m-n<=20这一个条件

    其实如果没有这个条件的话,我们就只能暴力去枚举了

    但是既然给出了这个条件,我们就要好好地利用

    边数最多只比点数多20,说明这一个图是非常接近一棵树的

    如果是树的话,那我们就可以直接用一个LCA维护就可以了

    但是这道题中的图要比正常的树多几条边

    所以我们可以考虑先来一个最小生成树(随便生成一个树也可以)

    这样我们就可以把n-1条边搞定

    剩下的m-n+1条边,我们就可以分别以这两条边的两个端点为起点跑最短路

    同时把这些点建立一个map映射

    这样最短路最多会跑42次

    在每一次处理两个点时,我们就可以拿这两个点的LCA

    和这两个点到map映射那些点的距离之和中取最小值

    需要注意的是,最小生成树和最短路不能用一个图,因为你最小生成树sort之后边就不对应了

    所以要提前保存好

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<map>
    using namespace std;
    typedef long long ll;
    const int maxn=2e5+50;
    map<ll,ll> ma;
    struct asd{
        ll from,to,next;
        ll val;
    }b[maxn*2],b2[maxn*2],b3[maxn*2];
    ll tot=2,head[maxn];
    ll diss[50][maxn];
    ll bh;
    inline void ad(ll aa,ll bb,ll cc){
        b[tot].from=aa;
        b[tot].to=bb;
        b[tot].next=head[aa];
        b[tot].val=cc;
        head[aa]=tot++;
    }
    ll h2[maxn],t2=2;
    inline void ad2(ll aa,ll bb,ll cc){
        b2[t2].from=aa;
        b2[t2].to=bb;
        b2[t2].next=h2[aa];
        b2[t2].val=cc;
        h2[aa]=t2++;
    }
    ll h3[maxn],t3=2;
    inline void ad3(ll aa,ll bb,ll cc){
        b3[t3].from=aa;
        b3[t3].to=bb;
        b3[t3].next=h3[aa];
        b3[t3].val=cc;
        h3[aa]=t3++;
    }
    //以上是建边,分别对应原图(跑最短路)、LCA、最小生成树
    struct jie{
    	ll num;
    	ll dis;
    	jie(ll aa=0,ll bb=0){
    		num=aa,dis=bb;
    	}
    	bool operator < (const jie& A) const{
    		return dis>A.dis;
    	}
    };
    ll vis[maxn];
    void dij(ll xx){
        priority_queue<jie> q;
    	q.push(jie(xx,0));
    	diss[ma[xx]][xx]=0;
        memset(vis,0,sizeof(vis));
    	while(!q.empty()){
    		ll now=q.top().num;
    		q.pop();
    		if(vis[now]) continue;
    		vis[now]=1;
    		for(ll i=head[now];i!=-1;i=b[i].next){
    			ll u=b[i].to;
    			if(diss[ma[xx]][u]>b[i].val+diss[ma[xx]][now]){
    				diss[ma[xx]][u]=b[i].val+diss[ma[xx]][now];
    				q.push(jie(u,diss[ma[xx]][u]));
    			}
    		}
    	}
    }
    //dij模板
    ll f[maxn][25],dep[maxn];
    ll cost[maxn][25];
    void dfs(ll now,ll fa,ll da){
        dep[now]=dep[fa]+1;
        f[now][0]=fa;
        cost[now][0]=da;
        for(ll i=1;(1<<i)<=dep[now];i++){
            f[now][i]=f[f[now][i-1]][i-1];
            cost[now][i]=cost[now][i-1]+cost[f[now][i-1]][i-1];
        }
        for(ll i=h2[now];i!=-1;i=b2[i].next){
            ll u=b2[i].to;
            if(fa!=u) dfs(u,now,b2[i].val);
        }
    }
    ll Lca(ll aa,ll bb){
        if(dep[aa]>dep[bb]) swap(aa,bb);
        ll len=dep[bb]-dep[aa],k=0;
        ll ans=0;
        while(len){
            if(len&1){
                ans+=cost[bb][k];
                bb=f[bb][k];
            }
            k++,len>>=1;
        }
        if(aa==bb) return ans;
        for(ll i=20;i>=0;i--){
            if(f[aa][i]==f[bb][i]) continue;
            ans+=cost[aa][i];
            ans+=cost[bb][i];
            aa=f[aa][i],bb=f[bb][i];
        }
        return ans+cost[aa][0]+cost[bb][0];
    }
    //倍增求LCA模板
    ll zx[maxn*2],visb[maxn*2];
    ll zhao(ll xx){
        if(xx==zx[xx]) return xx;
        return zx[xx]=zhao(zx[xx]);
    }
    void bing(ll xx,ll yy){
        zx[zhao(xx)]=zhao(yy);
    }
    bool cmp(asd aa,asd bb){
        return aa.val<bb.val;
    }
    void shu(ll xx){
        sort(b3+2,b3+t3,cmp);
        for(ll i=0;i<maxn;i++) zx[i]=i;
        ll cnt=0;
        for(ll i=2;i<t3;i++){
            ll x=b3[i].from,y=b3[i].to;
            if(zhao(x)!=zhao(y)){
                bing(x,y);
                ad2(x,y,b3[i].val);
                ad2(y,x,b3[i].val);
                visb[i]=1;
                if(++cnt==xx) return;
            }
        }
    }
    //最小生成树模板
    int main(){
        memset(diss,0x3f,sizeof(diss));
        memset(head,-1,sizeof(head));
        memset(h2,-1,sizeof(h2));
        memset(h3,-1,sizeof(h3));
        ll n,m;
        scanf("%lld%lld",&n,&m);
        for(ll i=1;i<=m;i++){
            ll aa,bb;
            ll cc;
            scanf("%lld%lld%lld",&aa,&bb,&cc);
            ad(aa,bb,cc),ad(bb,aa,cc);
            ad3(aa,bb,cc);
        }
        shu(n-1);
        for(ll i=2;i<t3;i++){
            if(!visb[i]){
                visb[i]=1;
                ll x=b3[i].from,y=b3[i].to;
                if(!ma[x]) ma[x]=++bh,dij(x);
                if(!ma[y]) ma[y]=++bh,dij(y);
            }
        }
        //把没有加到最小生成树中的边的两个端点拿出来跑最短路
        dfs(1,0,0);
        ll q;
        scanf("%lld",&q);
        for(ll i=1;i<=q;i++){
            ll aa,bb;
            scanf("%d%d",&aa,&bb);
            ll ans=Lca(aa,bb);
            for(ll i=1;i<=bh;i++){
                ans=min(ans,diss[i][aa]+diss[i][bb]);
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    
  • 相关阅读:
    leetcode刷题笔记十四 最长公共前缀 Scala版本
    leetcode刷题笔记十三 罗马数字转数字 Scala版本
    leetcode刷题笔记十二 整数转罗马数字 Scala版本
    Maven 安装与配置
    Maven基础
    java 打jar包配置文件和jar包通级
    java 类
    java 数组
    java 重载
    java 普通项目的配置文件
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/12856285.html
Copyright © 2011-2022 走看看