bzoj#2125. 最短路
题目描述
Description
给一个N个点M条边的连通无向图,满足每条边最多属于一个环,有Q组询问,每次询问两点之间的最短路径。
Input
输入的第一行包含三个整数,分别表示N和M和Q 下接M行,每行三个整数v,u,w表示一条无向边v-u,长度为w 最后Q行,每行两个整数v,u表示一组询问
Output
输出Q行,每行一个整数表示询问的答案
Solution
题意就是求仙人掌中两点最短路。
就是仙人掌圆方树的板子题。
对于每一个环用一个方点 t t t代替环,从方点 t t t向这个环的根节点 x x x连一条代价为 0 0 0的边,再从 t t t向环上其他所有点 y y y连一条代价为 m i n d i s ( x , y ) mindis(x,y) mindis(x,y)的边,并在 t t t上记录整个环的总长度 s u m t sum_t sumt。
每一次询问
(
x
,
y
)
(x,y)
(x,y)最短路时,分类讨论
L
C
A
(
x
,
y
)
LCA(x,y)
LCA(x,y)是方点还是圆点。
若是圆点答案即为
d
i
s
t
(
x
,
y
)
dist(x,y)
dist(x,y)。
若是方点,则答案为
x
x
x到环的最短路+
y
y
y到环的最短路+
x
x
x接入环的节点与
y
y
y接入环的节点之间的最短路。
因此只要倍增或树剖预处理出LCA与路径和,每次跳LCA的时候顺路求解答案即可。
时间复杂度 O ( n lg n ) O(n\lg n) O(nlgn)
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <cassert>
#include <string.h>
//#include <unordered_set>
//#include <unordered_map>
//#include <bits/stdc++.h>
#define MP(A,B) make_pair(A,B)
#define PB(A) push_back(A)
#define SIZE(A) ((int)A.size())
#define LEN(A) ((int)A.length())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define fi first
#define se second
using namespace std;
template<typename T>inline bool upmin(T &x,T y) { return y<x?x=y,1:0; }
template<typename T>inline bool upmax(T &x,T y) { return x<y?x=y,1:0; }
typedef long long ll;
typedef unsigned long long ull;
typedef long double lod;
typedef pair<int,int> PR;
typedef vector<int> VI;
const lod eps=1e-11;
const lod pi=acos(-1);
const int oo=1<<30;
const ll loo=1ll<<62;
const int mods=1e9+7;
const int MAXN=30005;
const int INF=0x3f3f3f3f;//1061109567
/*--------------------------------------------------------------------*/
inline int read()
{
int f=1,x=0; char c=getchar();
while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }
return x*f;
}
struct enode{int to,c; };
vector<enode> e[MAXN],E[MAXN];
int DFN=0,n,m,Case,num,dist[MAXN],sum[MAXN],Fa[MAXN],flag[MAXN];
int dfn[MAXN],low[MAXN],fa[MAXN][15],sm[MAXN][15],dep[MAXN],Log[MAXN];
void add_edge(int x,int y,int c)
{
// cout<<"Edge:"<<x<<" "<<y<<" "<<c<<endl;
E[x].PB(((enode){y,c}));
E[y].PB(((enode){x,c}));
}
void solve(int x,int y,int c)
{
sum[++num]=dist[y]-dist[x]+c;
add_edge(x,num,0);
for (int p=y;p!=x;p=Fa[p])
{
int Dis=min(dist[p]-dist[x],sum[num]-(dist[p]-dist[x]));
add_edge(p,num,Dis);
flag[p]=(Dis==(dist[p]-dist[x]));
}
}
void tarjan(int x,int father)
{
dfn[x]=low[x]=++DFN,Fa[x]=father;
for (auto V:e[x])
{
int v=V.to,c=V.c;
if (!dfn[v]) dist[v]=dist[x]+c,tarjan(v,x),upmin(low[x],low[v]);
else if (v!=father) upmin(low[x],dfn[v]);
if (dfn[x]<low[v]) add_edge(x,v,c);
}
for (auto V:e[x])
{
int v=V.to,c=V.c;
if (Fa[v]!=x&&dfn[v]>dfn[x]) solve(x,v,c);
}
}
void dfs(int x,int father)
{
dep[x]=dep[father]+1;
for (int i=1;i<=Log[dep[x]];i++)
fa[x][i]=fa[fa[x][i-1]][i-1],sm[x][i]=sm[fa[x][i-1]][i-1]+sm[x][i-1];
for (auto v:E[x])
{
if (v.to==father) continue;
fa[v.to][0]=x;
sm[v.to][0]=v.c;
dfs(v.to,x);
}
}
int getans(int x,int y)
{
int s=0;
if (dep[x]<dep[y]) swap(x,y);
for (int i=Log[dep[x]];i>=0;i--)
if (dep[fa[x][i]]>=dep[y]) s+=sm[x][i],x=fa[x][i];
// cout<<x<<" "<<y<<" "<<fa[x][0]<<" "<<s<<endl;
if (x==y) return s;
for (int i=Log[dep[x]];i>=0;i--)
if (fa[x][i]!=fa[y][i]) s+=sm[x][i]+sm[y][i],x=fa[x][i],y=fa[y][i];
// cout<<x<<" "<<y<<" "<<fa[x][0]<<" "<<s<<endl;
if (fa[x][0]<=n) return s+sm[x][0]+sm[y][0];
int Dis;
if (flag[x]^flag[y]) Dis=sm[x][0]+sm[y][0];
else Dis=abs(sm[x][0]-sm[y][0]);
return s+min(Dis,sum[fa[x][0]]-Dis);
}
int main()
{
n=read(),m=read(),Case=read(),num=n;
for (int i=1;i<=m;i++)
{
int u=read(),v=read(),c=read();
e[u].PB(((enode){v,c}));
e[v].PB(((enode){u,c}));
}
tarjan(1,0);
dep[0]=-1,Log[1]=0;
for (int i=2;i<=num;i++) Log[i]=Log[i>>1]+1;
dfs(1,0);
// for (int i=1;i<=num;i++) cout<<fa[i][0]<<" "<<sm[i][0]<<" "<<fa[i][1]<<" "<<sm[i][1]<<endl;
while (Case--)
{
int x=read(),y=read();
printf("%d\n",getans(x,y));
}
return 0;
}
/*
9 10 2
1 2 1
1 4 1
3 4 1
2 3 1
3 7 1
7 8 2
7 9 2
1 5 3
1 6 4
5 6 1
1 9
5 7
5
6
*/