题目描述

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\n",ans);
}
return 0;
}

The Shortest Statement CodeForces - 1051F 最小生成树+并查集+LCA的更多相关文章

  1. The Shortest Statement CodeForces - 1051F(待测试)

    #include <iostream> #include <cstdio> #include <sstream> #include <cstring> ...

  2. Mobile Phone Network CodeForces - 1023F(并查集lca+修改环)

    题意: 就是有几个点,你掌控了几条路,你的商业对手也掌控了几条路,然后你想让游客都把你的所有路都走完,那么你就有钱了,但你又想挣的钱最多,真是的过分..哈哈 游客肯定要对比一下你的对手的路 看看那个便 ...

  3. UVA 1395 苗条的生成树(最小生成树+并查集)

    苗条的生成树 紫书P358 这题最后坑了我20分钟,怎么想都对了啊,为什么就wa了呢,最后才发现,是并查集的编号搞错了. 题目编号从1开始,我并查集编号从0开始 = = 图论这种题真的要记住啊!!题目 ...

  4. CSP 201703-4 地铁修建【最小生成树+并查集】

    问题描述 试题编号: 201703-4 试题名称: 地铁修建 时间限制: 1.0s 内存限制: 256.0MB 问题描述: 问题描述 A市有n个交通枢纽,其中1号和n号非常重要,为了加强运输能力,A市 ...

  5. hdu 2874 Connections between cities (并查集+LCA)

    Connections between cities Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (J ...

  6. hdu6074[并查集+LCA+思维] 2017多校4

    看了标答感觉思路清晰了许多,用并查集来维护全联通块的点数和边权和. 用另一个up[]数组(也是并查集)来保证每条边不会被重复附权值,这样我们只要将询问按权值从小到大排序,一定能的到最小的边权和与联通块 ...

  7. Codeforces 731C Socks 并查集

    题目:http://codeforces.com/contest/731/problem/C 思路:并查集处理出哪几堆袜子是同一颜色的,对于每堆袜子求出出现最多颜色的次数,用这堆袜子的数目减去该值即为 ...

  8. codeforces 722C (并查集)

    题目链接:http://codeforces.com/contest/722/problem/C 题意:每次破坏一个数,求每次操作后的最大连续子串和. 思路:并查集逆向操作 #include<b ...

  9. 关于最小生成树(并查集)prime和kruskal

    适合对并查集有一定理解的人.  新手可能看不懂吧.... 并查集简单点说就是将相关的2个数字联系起来 比如 房子                      1   2    3   4  5   6 ...

随机推荐

  1. monkey命令的基本使用

    看到monkey,你想到了什么?今天给大家分享下monkey命令的基本使用 monkey测试是Android平台自动化测试的一种手段,通过monkey程序模拟用户触摸屏幕,滑动.按键操作等操作对设备上 ...

  2. Python模拟用户登录场景

    简单模拟登录场景,将已知的用户名及密码固化,通过用户输入内容和已固化的内容比较进行判断用户名和密码是否输入正确. 在用户输入时,将密码隐藏需要导入模块getpass import getpass _u ...

  3. Linux: ssh命令 远程登录

    1.查看SSH客户端版本 使用ssh -V命令可以得到版本号.需要注意的是,Linux一般自带的是OpenSSH; $ ssh -V ssh: SSH Secure Shell 3.2.9.1 (no ...

  4. Redis Desktop Manager无法连接虚拟机中启动的redis服务问题排查步骤

    Redis Desktop Manager下载地址 https://redisdesktop.com/download 安装好以后连接linux服务器上的Redis服务器错误: 问题排查: 1.检查R ...

  5. SpringCloud Alibaba (一):序言

    为什么要转用SpringCloud Alibaba? Spring Cloud Netflix项目进入维护模式 在2018年底时,Netflix宣布Hystrix进入维护模式.自2016年以来,Rib ...

  6. 多应用下 Swagger 的使用,这可能是最好的方式!

    问题 微服务化的时代,我们整个项目工程下面都会有很多的子系统,对于每个应用都有暴露 Api 接口文档需要,这个时候我们就会想到 Swagger 这个优秀 jar 包.但是我们会遇到这样的问题,假如说我 ...

  7. jmeter对数据库进行简单的压测

    1.点击测试计划,再点击“浏览”,把JDBC驱动添加进来: 注:JDBC驱动一般的位置在java的安装地址下,路径类似于:    \java\jre\lib\ext 文件为:mysql-connect ...

  8. 消息队列——RabbitMQ的基本使用及高级特性

    文章目录 一.引言 二.基本使用 1. 简单示例 2. work queue和公平消费消息 3. 交换机 三.高级特性 1. 消息过期 2. 死信队列 3. 延迟队列 4. 优先级队列 5. 流量控制 ...

  9. Oracle调用Java方法(上)如何使用LoadJava命令和如何将简单的Jar包封装成Oracle方法

    最近在工作中遇到了遇到了一个需求需要将TIPTOP中的数据导出成XML并上传到FTP主机中,但是4GL这方面的文档比较少最终决定使用Oracle调用Java的方法,在使用的过程中发现有很多的坑,大部分 ...

  10. Java中时间处理

    旧 API:位于 java.util 包中,里面主要有 Date.Calendar.TimeZone 类 新 API:位于 java.time 包中,里面主要有 LocalDateTime.Zoned ...