bzoj#2125. 最短路

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
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值