首页 > 其他分享 >[题解] CF1051F The Shortest Statement

[题解] CF1051F The Shortest Statement

时间:2023-11-14 19:56:23浏览次数:39  
标签:int 题解 fa Read MAXN Statement 非树边 Shortest dis

The Shortest Statement

给一张 \(n\) 个点 \(m\) 条边的无向连通图,保证 \(m - n \le 20\),\(q\) 次询问求两个点间的最短路。
\(n, m, q \le 10^5\)。

由于边数只比点数多 20,所以如果我们建出这张图的一棵生成树,那么非树边至多有 21 条。

那么现在两点之间的最短路就转化成了不经过非树边的和经过非树边的最短路取 min。不经过非树边的就是树上两点之间的路径,经过非树边的就是枚举每一条非树边的端点,强制经过它,用 \(dis_{u, k} + dis_{k, v}\) 来更新答案。这部分可以直接 Dijkstra 预处理。

时间复杂度 \(O((n - m) \log m + q \log n)\)。

constexpr int MAXN = 1e5 + 9, MAXLG = 17, MAXK = 50;
constexpr ll INF = 1e18;
int n, m, q, fa[MAXLG][MAXN], dep[MAXN], k;
ll dis[MAXN], dist[MAXK][MAXN];
vpii G[MAXN];
bool vis[MAXN], mark[MAXN];

void dfs(int u, int ft) {
	dep[u] = dep[fa[0][u] = ft] + 1, vis[u] = true;
	for (int i = 1; i <= 16; i ++)
		fa[i][u] = fa[i - 1][fa[i - 1][u]];
	for (auto [v, w] : G[u]) {
		if (v == ft) continue;
		if (vis[v]) { mark[u] = mark[v] = true; continue; }
		dis[v] = dis[u] + w, dfs(v, u);
	}
	return;
}
int Get_Lca(int u, int v) {
	if (dep[u] < dep[v]) swap(u, v);
	for (int i = 16; ~i; i --)
		if (dep[fa[i][u]] >= dep[v])
			u = fa[i][u];
	if (u == v) return u;
	for (int i = 16; ~i; i --)
		if (fa[i][u] != fa[i][v])
			u = fa[i][u], v = fa[i][v];
	return fa[0][u];
}
ll Get_Dis(int u, int v) {
	int lca = Get_Lca(u, v);
	return dis[u] + dis[v] - 2 * dis[lca];
}

void Dijkstra(int s) {
	priority_queue<pli, vector<pli>, greater<pli> > q;
	for (int i = 1; i <= n; i ++) dist[k][i] = INF, vis[i] = false;
	q.emplace(dist[k][s] = 0, s);
	while (q.size()) {
		int u = q.top().sec; q.pop();
		if (vis[u]) continue; vis[u] = true;
		for (auto [v, w] : G[u])
			if (dist[k][u] + w < dist[k][v]) {
				dist[k][v] = dist[k][u] + w;
				if (!vis[v]) q.emplace(dist[k][v], v);
			}
	}
	return;
}

void slv() {
	n = Read<int>(), m = Read<int>();
	for (int i = 1; i <= m; i ++) {
		int u = Read<int>(), v = Read<int>(), w = Read<int>();
		G[u].emplace_back(v, w), G[v].emplace_back(u, w);
	}
	dfs(1, 0);
	for (int i = 1; i <= n; i ++)
		if (mark[i]) k ++, Dijkstra(i);
	q = Read<int>();
	while (q --) {
		int u = Read<int>(), v = Read<int>();
		ll ans = Get_Dis(u, v);
		for (int i = 1; i <= k; i ++)
			cmin(ans, dist[i][u] + dist[i][v]);
		Write(ans, '\n');
	}
	return;
}

标签:int,题解,fa,Read,MAXN,Statement,非树边,Shortest,dis
From: https://www.cnblogs.com/definieren/p/17832396.html

相关文章

  • 「NOIP2014」解方程 题解
    思路首先我们可以观察到\(n\)和\(m\)与\(a_i\)相比小的很多,所以我们可以考虑直接暴力求解但是\(a_i\)太大了,所以如果需要直接计算的话需要全程使用高精度算法。因为高精度算法代码量有大速度又慢我们可依考虑将\(a_i\)转化为一个极大的指数取模的结果,因为只有是模数的......
  • Q7.4.1.2. 奇怪的方格涂色 题解
    原题链接首先想到暴力网络流:考虑最小割,\(S\)表示染黑色,\(T\)表示染白色。每个格子\(i\),连\((S,i,b_i)\),\((i,T,w_i)\)。怎么处理“奇怪的方格”?连\((i,i^\prime,p_i)\)和\((i^\prime,j,+\infty)\)。表示如果\(i\)归属于\(S\)(染黑色),那么就只能忍受奇怪所带来的\(p_i\)......
  • AT_abc230_f [ABC230F] Predilection 题解
    prelogue各位在比赛的时候一定要坚信自己的式子,然后去考虑自己的实现是不是挂了。本人在今天模拟赛的时候质疑自己的式子然后不看实现100->0。analysis考虑对这个给定数组进行前缀和,然后就将问题转化成为了求这个前缀和数组的子序列的个数。对于求子序列,我们很轻松可以写出......
  • Codeforces Round 809 (Div. 2) D1. Chopping Carrots (Easy Version) 题解
    题意CodeforcesRound809(Div.2)D1.ChoppingCarrots(EasyVersion)给两个整数\(n,k\),一个数组\(a\),要求构造一个同样长度的数组\(p\),使得\(\max\limits_{1\lei\len}\left(\left\lfloor\frac{a_i}{p_i}\right\rfloor\right)-\min\limits_{1\lei\l......
  • [USACO23FEB] Equal Sum Subarrays G 题解
    [USACO23FEB]EqualSumSubarraysG题解题目链接\(O(n^5)\)暴力显然,如果修改\(a_i\)的值,只会影响包含\(a_i\)的区间的区间和。于是对于每个\(a_i\),可以将所有区间分成两类,即包含\(a_i\)的区间和不包含\(a_i\)的区间。这两种区间的区间和中最小的差值即为答案。......
  • 【洛谷 P1307】[NOIP2011 普及组] 数字反转 题解(字符串)
    [NOIP2011普及组]数字反转题目描述给定一个整数,请将该数各个位上数字反转得到一个新数。新数也应满足整数的常见形式,即除非给定的原数为零,否则反转后得到的新数的最高位数字不应为零(参见样例2)。输入格式一个整数。输出格式一个整数,表示反转后的新数。样例#1样例输入#1123样......
  • AGC041D-Problem Scores 题解
    题目链接luoguatcoder分析令\(k=\left\lfloor\frac{n}{2}\right\rfloor\)对于第三个条件,只需要满足\(\sum_{i=1}^{k+1}a[i]<\sum_{i=n-k+1}^{n}a[i]\)即可有一个\(trick\):构造一个单调不降或不增的序列可以转化为每次做一次前缀加操作例如本题要求构造一个单调......
  • [题解] CF1748E Yet Another Array Counting Problem
    YetAnotherArrayCountingProblem给你一个长度为\(n\)的序列和一个数\(m\),求有多少个长度为\(n\)的序列\(b\)满足:\(\foralli\in[1,n],b_i\in[1,m]\)。对于每个区间\([l,r]\),\(b\)中最大值最靠左的位置和\(a\)相同。\(n,m\le2\times10^5,n\ti......
  • [题解] P4435 [COCI2017-2018#2] ​​Garaža
    P4435[COCI2017-2018#2]Garaža给你一个长度为\(n\)的序列\(a\),单点改,查询区间\(\gcd\)不为1的子区间个数。\(n,Q\le10^5,a_i\le10^9\)。先看单次全局查询怎么做。考虑一个分治,每次我们要计算跨过分治中心\(mid\)的答案。因为这个是单调的,所以可以双指针做......
  • 【题解】P4768 [NOI2018] 归程 / Kruskal 重构树
    补补以前懒得总结的零碎东西。kruskal重构树使用条件:求无向图中两点之间所有路径的最大边权的最小值构造:依kruskal得到最小生成树从小到大考虑生成树中的边\((u,v)\)对于\((u,v)\),新建一个结点,作为重构树中\(u,v\)的父结点该结点的点权为\((u,v)\)的......