CF1051F The Shortest Statement

CF1051F The Shortest Statement

题目描述

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

You should answer q q queries, the \(i\) -th query is to find the shortest distance between vertices \(u_i\) and \(v_i\).

输入输出格式

输入格式:

The first line contains two integers \(n\) and \(m\) \((1 \le n, m \le 10^5, m - n \le 20)\) — the number of vertices and edges in the graph.

Next \(m\) lines contain the edges: the \(i\) -th edge is a triple of integers \(v_i, u_i, d_i\) \((1 \le u_i, v_i \le n, 1 \le d_i \le 10^9, u_i \neq v_i)\).

This triple means that there is an edge between vertices \(u_i\) and \(v_i\) of weight \(d_i\) . It is guaranteed that graph contains no self-loops and multiple edges.

The next line contains a single integer \(q~(1 \le q \le 10^5)\) — the number of queries.

Each of the next \(q\) lines contains two integers \(u_i\) and \(v_i~(1 \le u_i, v_i \le n)\) — descriptions of the queries.

Pay attention to the restriction \(m - n ~ \le ~ 20\).

输出格式:

Print \(q\) lines.

The \(i\) -th line should contain the answer to the \(i\) -th query — the shortest distance between vertices \(u_i\) and \(v_i\).


不错的题目

发现Pay \ attention...,所以肯定有用了(一般看数据范围这么奇怪也会想到吧

思路:先跑一颗生成树,然后把不在生成树的边的端点拿出来,以每个端点跑最短路。询问直接枚举经过哪个端点或者直接是树上路径


Code:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define ll long long
const int N=1e5+10;
int n,m,Q;
int head[N],to[N<<1],Next[N<<1],cnt=1;
ll edge[N<<1],dis[N];
void add(int u,int v,ll w)
{
    to[++cnt]=v,Next[cnt]=head[u],edge[cnt]=w,head[u]=cnt;
}
int is[N<<1],used[N],f[N][21],dep[N];
void dfs(int now)
{
    for(int i=1;f[now][i-1];i++) f[now][i]=f[f[now][i-1]][i-1];
    used[now]=1;
    for(int i=head[now];i;i=Next[i])
    {
        int v=to[i];
        if(!used[v])
        {
            is[i]=is[i^1]=1;
            dis[v]=dis[now]+edge[i];
            f[v][0]=now;
            dep[v]=dep[now]+1;
            dfs(v);
        }
    }
}
int LCA(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=20;~i;i--)
        if(dep[f[x][i]]>=dep[y])
            x=f[x][i];
    if(x==y) return x;
    for(int i=20;~i;i--)
        if(f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];
    return f[x][0];
}
int point[100],tot;
#define P pair <ll,int>
priority_queue <P,vector <P>,greater <P> > q;
ll diss[100][N];
void dijs()
{
    memset(diss,0x3f,sizeof(diss));
    for(int i=1;i<=tot;i++)
    {
        int u=point[i];
        diss[i][u]=0;
        q.push(make_pair(0,u));
        memset(used,0,sizeof(used));
        while(!q.empty())
        {
            int now=q.top().second;
            q.pop();
            if(used[now]) continue;
            used[now]=1;
            for(int j=head[now];j;j=Next[j])
            {
                int v=to[j];
                if(diss[i][v]>diss[i][now]+edge[j])
                {
                    diss[i][v]=diss[i][now]+edge[j];
                    q.push(make_pair(diss[i][v],v));
                }
            }
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    ll w;
    for(int u,v,i=1;i<=m;i++)
    {
        scanf("%d%d%lld",&u,&v,&w);
        add(u,v,w),add(v,u,w);
    }
    dfs(1);
    for(int u=1;u<=n;u++)
        for(int i=head[u];i;i=Next[i])
        {
            int v=to[i];
            if(!is[i]) point[++tot]=u,point[++tot]=v;
        }
    sort(point+1,point+1+tot);
    tot=unique(point+1,point+1+tot)-point-1;
    dijs();
    scanf("%d",&Q);
    for(int u,v,i=1;i<=Q;i++)
    {
        scanf("%d%d",&u,&v);
        int lca=LCA(u,v);
        ll ans=dis[u]+dis[v]-dis[lca]*2;
        for(int j=1;j<=tot;j++)
            ans=min(ans,diss[j][u]+diss[j][v]);
        printf("%lld\n",ans);
    }
    return 0;
}

2018.10.10

posted @ 2018-10-10 08:00  露迭月  阅读(179)  评论(0编辑  收藏  举报