题目描述

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. Jmeter让压测随时做起来(转载)

    为什么要压测 这个问题问的其实挺没有必要的,做开发的同学应该都很清楚,压测的必要性,压力测试主要目的就是让我们在上线前能够了解到我们系统的承载能力,和当前.未来系统压力的提升情况,能够评估出当前系统的 ...

  2. Github上可以涨薪30k的Java教程和实战项目终于可以免费下载了

    写在前面 大家都知道 Github 是一个程序员福地,这里有各种厉害的开源框架.软件或者教程.这些东西对于我们学习和进步有着莫大的进步,所以我有了这个将 Github 上非常棒的 Java 开源项目整 ...

  3. python flask API 返回状态码

    @app.route('/dailyupdate', methods = ['POST','GET'])def dailyUpdate(): try: db=MySQLdb.connect(" ...

  4. Hive和HBase整合用户指南

    本文讲解的Hive和HBase整合意思是使用Hive读取Hbase中的数据.我们可以使用HQL语句在HBase表上进行查询.插入操作:甚至是进行Join和Union等复杂查询.此功能是从Hive 0. ...

  5. (三)Maven命令列表

    mvn –version 显示版本信息 mvn clean 清理项目生产的临时文件,一般是模块下的target目录 mvn compile 编译源代码,一般编译模块下的src/main/java目录, ...

  6. .Net Core Configuration源码探究

    前言     上篇文章我们演示了为Configuration添加Etcd数据源,并且了解到为Configuration扩展自定义数据源还是非常简单的,核心就是把数据源的数据按照一定的规则读取到指定的字 ...

  7. Linux 初始化系统 SystemV Upstart

    System V 特点 缺点: 启动时间长,init是串行启动,只有前一个进程启动完,才会启动下一个进程 启动脚本复杂,init只是执行启动脚本,不管其他事情,脚本需要自己处理各种情况,这往往使得脚本 ...

  8. 《Elasticsearch 权威指南》阅读笔记

    书籍地址 https://www.elastic.co/guide/cn/elasticsearch/guide/current/languages.html

  9. TestNG配合ant脚本进行单元测试

    上面就是一个简单的SSM框架的整合,数据库来自宜立方商城的e3-mall采用一个简单的spring-mvc和spring以及mybatis的整合 单元测试代码为 TestUserByTestNG.ja ...

  10. easymock笔记2

    EasyMock主要是为测试提供模拟数据,比如你可以模拟HttpServletRequest. EasyMock 可以mock interface和抽象java 类,但是不可以mock拥有被final ...