[CF1051F]The Shortest Statement

题目

传送门 to luogu

思路

奇怪的限制 m n 20 m-n\le 20 是什么鬼?

再看看,保证原图连通。可以隐隐约约感觉到,这个图很稀疏。稀疏到两个点之间快要 只有一条路径 了。稀疏到 可以看成一颗树上添加了极少的边

树上的最短路是让人感到愉悦的问题,因为根本只有一条路。那么多出来的极少的边怎么办?

考虑一条可能的 s s t t 的最短路,不妨设其经过了非树边(因为全是树边的情况可以直接求出啊)。它第一次经过非树边之前,只能走树上路径,然后走到了一个 非树边的端点。接下来从这个点出发,随便的行走,然后走到终点。

也就是说,如果最短路经过了非树边,那么 一定有一个中转点是非树边的端点

枚举端点可行吗?这是 O ( m n ) \mathcal O(m-n) 的。显然可行。对于每个端点都做一次最短路,每个询问的时候进行查询即可。复杂度是 O [ ( m n ) ( m log m + q ) + q log n ] \mathcal O[(m-n)(m\log m+q)+q\log n] 的。

——个人认为我的写法中 d i j k s t r a \tt dijkstra 跟边数有关系,一般我都写 O ( m log m ) \mathcal O(m\log m) 的。

代码

#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#include <bitset>
using namespace std;
typedef long long int_;
inline int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MaxN = 100005;
const int MaxM = MaxN<<1;
int n, m;

struct Edge{
	int to, nxt, val;
	Edge(){ }
	Edge(int T,int N,int V){
		to = T, nxt = N, val = V;
	}
};
Edge e[MaxM];
int head[MaxN], cntE;
/** @param save If true, keep origin. */
void clear_graph(bool save = false){
	for(int i=1; i<=n; ++i)
		head[i] = -1;
	if(!save) cntE = 0;
}
/** @brief Add a directed edge. */
void addEdge(int a,int b,int c){
	e[cntE] = Edge(b,head[a],c);
	head[a] = cntE ++;
}

int fa[MaxN][20], dep[MaxN];
int_ dist[MaxN]; // 边权的前缀和
bool used[MaxM]; // 是否为非树边
/** @brief Construct a spanning tree. */
void build(int x){
	for(int j=0; j+1<20; ++j)
		fa[x][j+1] = fa[fa[x][j]][j];
	for(int i=head[x]; ~i; i=e[i].nxt){
		if(e[i].to == fa[x][0])
			continue; // no backwards
		if(fa[e[i].to][0] == 0){
			fa[e[i].to][0] = x;
			dep[e[i].to] = dep[x]+1;
			dist[e[i].to] = dist[x];
			dist[e[i].to] += e[i].val;
			build(e[i].to);
		}
		else used[i] = true;
	}
}
/** @return @p a 和 @p b 的树上距离 */
int_ getDis(int a,int b){
	int_ res = dist[a]+dist[b];
	if(dep[a] < dep[b]) swap(a,b);
	for(int j=19; ~j; --j)
		if(dep[fa[a][j]] >= dep[b])
			a = fa[a][j];
	if(a == b)
		return res-(dist[b]<<1);
	for(int j=19; ~j; --j)
		if(fa[a][j] != fa[b][j]){
			a = fa[a][j];
			b = fa[b][j];
		}
	return res-(dist[fa[a][0]]<<1);
}

struct Node{
	int_ dis; int id;
	Node(int_ D,int I):dis(D),id(I){ }
	operator int_()const{return -dis;}
};
priority_queue< Node > pq;
const int_ infty = (1ll<<60)-1;
/** @brief 单源点最短路 */
void dijkstra(int from,int_ dis[]){
	for(int i=1; i<=n; ++i)
		dis[i] = infty;
	dis[from] = 0;
	pq.push(Node(0,from));
	while(!pq.empty()){
		Node t = pq.top(); pq.pop();
		if(t.dis > dis[t.id]) continue;
		for(int i=head[t.id]; ~i; i=e[i].nxt)
			if(dis[e[i].to] > t.dis+e[i].val){
				dis[e[i].to] = t.dis+e[i].val;
				pq.push(Node(dis[e[i].to],e[i].to));
			}
	}
}

const int MaxTot = 42;
int all[MaxTot]; // 所有特殊的点
int_ dis[MaxTot][MaxN]; // 特殊点的最短路

int main(){
	n = readint(), m = readint();
	clear_graph();
	for(int i=1,a,b,c; i<=m; ++i){
		a = readint(), b = readint();
		addEdge(a,b,c = readint());
		addEdge(b,a,c);
	}
	fa[1][0] = 1, build(1);
	int tot = 0;
	for(int i=0; i<(m<<1); ++i)
		if(used[i]) // 非树边
			all[tot ++] = e[i].to;
	sort(all,all+tot);
	tot = unique(all,all+tot)-all;
	for(int i=0; i<tot; ++i)
		dijkstra(all[i],dis[i]);
	int q = readint();
	for(int a,b; q; --q){
		a = readint(), b = readint();
		int_ c = getDis(a,b);
		for(int i=0; i<tot; ++i)
			c = min(c,dis[i][a]+dis[i][b]);
		printf("%lld\n",c);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42101694/article/details/108182856