原题的旅行
数据范围:\(n<=10^5,m<=2*10^5,q<=10^5,1<=w_i<=10^9\)
Solution
有一个比较简单粗暴的想法是先将文明城市之间的两两最短路预处理出来,然后建MST,对于一个询问直接在树上倍增就好了
但是现在的问题是这样的话边数是\(n^2\)的,所以我们考虑另一种建图方式
我们考虑对于每一个点\(x\)预处理出到它最近的文明城市是那个,以及它们之间的最短距离,分别记为\(from[x]\)和\(dis[x]\),然后对于原图中的一条边\((x,y,w)\),如果说\(from[x]\neq from[y]\),我们就往新图里面加一条\((from[x],from[y],dis[x]+w+dis[y])\)的边,这样总的边数是\(m\)的,然后在新图里面MST再倍增什么的就好了
这样是对的是因为:首先这样的一条边一定有机会成为MST中的一条边,然后现在我们只要考虑有没有遗漏的情况,假设\(a\)和\(b\)是两个文明城市,这两个城市之间的最短路是MST中的一条边,但是并没有被上面的那种方式枚举到
然后\(a\)和\(b\)(\(a\neq b\))间的最短路一定是由原图的边组成的,因为题目保证\(w_i\)(边权)\(>=1\)且\(a\neq b\),所以最短路中必定存在至少一条边\((u,v)\)满足\(from[u]\neq from[v]\),假设所有满足两个端点的\(from\)值不等的边中没有一条两端的\(from\)同时是\(a\)和\(b\),那么就说明这条路径上面至少有一个点到\(a\)或者到\(b\)的路径不是最短的,然后因为最短路径的传递性,这一条路自然也就不是最短路了,与我们前面的假设矛盾,所以一定不存在这样的遗漏情况
mark:预处理\(dis\)和\(from\)可以直接用\(dij\)做到\(O(nlogn)\),一开始的时候把所有的文明城市丢进堆里面就好了
代码大概长这个样子
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e5+10,M=2e5+10;
const ll inf=1LL<<60;
int mark[N];
int n,m,Q,cntgood;
namespace T{/*{{{*/
const int TOP=20;//just for debuging
struct xxx{
int y,nxt;
ll dis;
}a[N*2];
int h[N],f[N][TOP+1],dep[N];
ll mx[N][TOP+1];
int tot,n;
void init(int _n){memset(h,-1,sizeof(h)); tot=0; n=_n;}
void add(int x,int y,ll d){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot; a[tot].dis=d;}
void dfs(int fa,int x,int d){
int u;
dep[x]=d; f[x][0]=fa;
for (int i=1;i<=TOP;++i){
f[x][i]=f[f[x][i-1]][i-1];
mx[x][i]=max(mx[x][i-1],mx[f[x][i-1]][i-1]);
}
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa) continue;
mx[u][0]=a[i].dis;
dfs(x,u,d+1);
}
}
ll query(int x,int y){
ll ret=0;
if (dep[x]<dep[y]) swap(x,y);
for (int i=TOP;i>=0;--i)
if (dep[f[x][i]]>=dep[y]){
ret=max(ret,mx[x][i]);
x=f[x][i];
}
if (x==y) return ret;
for (int i=TOP;i>=0;--i)
if (f[x][i]!=f[y][i]){
ret=max(ret,mx[x][i]);
ret=max(ret,mx[y][i]);
x=f[x][i],y=f[y][i];
}
return max(ret,max(mx[x][0],mx[y][0]));
}
}/*}}}*/
namespace G{/*{{{*/
struct xxx{
int y,nxt,dis;
}a[M*2];
struct Edge{
int x,y;
ll dis;
Edge(){}
Edge(int x1,int y1,ll dis1){x=x1; y=y1; dis=dis1;}
friend bool operator < (Edge x,Edge y){return x.dis<y.dis;}
}rec[M];
struct Data{
int node,dis;
Data(){}
Data(int node1,int dis1){node=node1; dis=dis1;}
friend bool operator < (Data x,Data y){return x.dis>y.dis;}
};
priority_queue<Data> q;
int h[N],f[N],vis[N];
ll dis[N],from[N];
int tot,totrec;
void init(){memset(h,-1,sizeof(h)); tot=-1;}
void add(int x,int y,int d){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot; a[tot].dis=d;}
void dij(){
int u,v;
while (!q.empty()) q.pop();
for (int i=1;i<=n;++i) dis[i]=inf,vis[i]=0;
for (int i=1;i<=n;++i)
if (mark[i])
from[i]=i,dis[i]=0,q.push(Data(i,dis[i]));
while (!q.empty()){
v=q.top().node; q.pop();
if (vis[v]) continue;
vis[v]=1;
for (int i=h[v];i!=-1;i=a[i].nxt){
u=a[i].y;
if (vis[u]) continue;
if (dis[u]>dis[v]+a[i].dis){
dis[u]=dis[v]+a[i].dis;
from[u]=from[v];
q.push(Data(u,dis[u]));
}
}
}
}
int get_f(int x){return f[x]=f[x]==x?x:get_f(f[x]);}
void build(){
dij();
int x,y;
ll w;
for (int i=0;i<=tot;i+=2){
x=a[i^1].y; y=a[i].y; w=a[i].dis;
if (from[x]!=from[y])
rec[++totrec]=Edge(from[x],from[y],w+dis[x]+dis[y]);
}
for (int i=1;i<=n;++i) f[i]=i;
sort(rec+1,rec+1+totrec);
int cnt=0;
T::init(cntgood);
for (int i=1;i<=totrec&&cnt<cntgood-1;++i){
x=rec[i].x; y=rec[i].y; w=rec[i].dis;
if (get_f(x)==get_f(y)) continue;
f[f[x]]=f[y];
T::add(x,y,rec[i].dis);
T::add(y,x,rec[i].dis);
++cnt;
}
}
}/*}}}*/
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
int x,y,z;
char ch;
scanf("%d%d\n",&n,&m);
cntgood=0;
for (int i=1;i<=n;++i)
scanf("%c",&ch),mark[i]=ch=='1',cntgood+=mark[i];
G::init();
for (int i=1;i<=m;++i){
scanf("%d%d%d",&x,&y,&z);
G::add(x,y,z);
G::add(y,x,z);
}
G::build();
for (int i=1;i<=n;++i)
if (mark[i]){
T::dfs(0,i,1);
break;
}
scanf("%d",&Q);
for (int i=1;i<=Q;++i){
scanf("%d%d",&x,&y);
printf("%lld\n",T::query(x,y));
}
}