首页 > 其他分享 >CF1051F题解

CF1051F题解

时间:2024-07-11 12:41:30浏览次数:12  
标签:int 题解 短路 CF1051F lca define top dis

The Shortest Statement

算法:树链剖分,最小生成树,最短路。

先讲一下题意:有一个 \(n\) 点 \(m\) 边的无向连通图,\(q\) 次询问,每次询问 \(a\) 到 \(b\) 的最短路长度。

数据范围 \(1\le n,m\le 10^5,m-n\le 20\)。

首先发现给了一个很奇怪的限制:\(m-n\le 20\),考虑他有什么用。

我们在图上跑全源最短路显然会超时,但如果是一棵树呢?显然是好做的,写一个 \(lca\) 就行了。

所以我们先求出来原图的最小生成树,这需要 \(n-1\) 条边,那么剩下的边就不超过 \(m-(n-1)=21\) 条,所以至多连接了 \(21\times 2=42\) 个点。

于是我们对这些点跑单源最短路即可,使用 \(dijkstra\) 算法,显然不会超时。

下文把在最小生成树上的边称为树边,否则称为非树边,在非树边两端的点称为中转点。

总结一下,我们可以把 \(a\) 到 \(b\) 的最短路分成 \(2\) 类:

  • 不经过非树边:使用 \(lca\) 预处理就很好做了。

  • 经过非树边:用 \(dijkstra\) 预处理以中转点为源点的单源最短路,枚举转移即可。

下文给出了树链剖分求 \(lca\) 的方法,用倍增实现也是可行的。

这里说几个我在写这道题的代码中写出来的的错误:

  • \(h\) 数组未初始化为 \(-1\)。

  • 排序时排序 \(e\) 数组而不是 \(edge\) 数组。

  • \(dfs2\) 中循环内的递归写为 \(dfs2(j,u)\)。

#include<bits/stdc++.h>
#define int long long
#define N 100005
#define M 200005
#define K 45
#define pii pair<int,int>
#define x first
#define y second
using namespace std;
int n,m,Q,h[N],e[M],w[M],ne[M],idx;
int dep[N],fa[N],son[N],siz[N];
int top[N],dis[K][N];
int p[N],sum[N];
bool st[N];
vector<pii>E[N];
vector<int>spe;
struct node{
	int a,b,c;
	bool operator<(const node &t)const{
		return c<t.c;
	}
}edge[N];
int find(int x){
	if(p[x]!=x)p[x]=find(p[x]);
	return p[x];
}
void add(int a,int b,int c){
	e[idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx++;
}
void dfs1(int u,int f){
	fa[u]=f;
	if(f!=-1)dep[u]=dep[f]+1;
	siz[u]=1;
	for(int i=h[u];~i;i=ne[i]){
		int j=e[i];
		if(j==fa[u])continue;
		sum[j]=sum[u]+w[i];
		dfs1(j,u);
		siz[u]+=siz[j];
		if(!son[u]||siz[son[u]]<siz[j])son[u]=j;
	}
}
void dfs2(int u,int f){
	top[u]=f;
	if(son[u])dfs2(son[u],f);
	for(int i=h[u];~i;i=ne[i]){
		int j=e[i];
		if(j==fa[u]||j==son[u])continue;
		dfs2(j,j);
	}
}
int get_lca(int a,int b){
	while(top[a]!=top[b]){
		if(dep[top[a]]>=dep[top[b]])a=fa[top[a]];
		else b=fa[top[b]];
	}
	return dep[a]<dep[b]?a:b;
}
void dij(int s){
	memset(dis[s],0x3f,sizeof dis[s]);
	memset(st,0,sizeof st);
	priority_queue<pii,vector<pii>,greater<pii>>q;
	dis[s][spe[s]]=0;
	q.push({dis[s][spe[s]],spe[s]});
	while(!q.empty()){
		auto t=q.top().y;
		q.pop();
		if(st[t])continue;
		st[t]=1;
		for(auto eu:E[t]){
			int j=eu.x,c=eu.y;
			if(dis[s][j]>dis[s][t]+c){
				dis[s][j]=dis[s][t]+c;
				q.push({dis[s][j],j});
			}
		}
	}
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		p[i]=i;
	}
	memset(h,-1,sizeof h);
	for(int i=1;i<=m;i++){
		int a,b,c;
		cin>>a>>b>>c;
		edge[i]={a,b,c};
		E[a].push_back({b,c});
		E[b].push_back({a,c});
	}
	sort(edge+1,edge+m+1);
	for(int i=1;i<=m;i++){
		int a=edge[i].a,b=edge[i].b,c=edge[i].c;
		int x=find(a),y=find(b);
		if(x!=y){
			p[x]=y;
			add(a,b,c);
			add(b,a,c);
		}
		else{
			spe.push_back(a);
			spe.push_back(b);
		}
	}
	for(int i=0;i<spe.size();i++){
		dij(i);
	}
	dfs1(1,-1);
	dfs2(1,1);
	cin>>Q;
	while(Q--){
		int a,b;
		cin>>a>>b;
		int lca=get_lca(a,b);
		int res=sum[a]+sum[b]-sum[lca]*2;
		for(int i=0;i<spe.size();i++){
			res=min(res,dis[i][a]+dis[i][b]);
		}
		cout<<res<<'\n';
	}
	return 0;
}

标签:int,题解,短路,CF1051F,lca,define,top,dis
From: https://www.cnblogs.com/zxh923aoao/p/18295909

相关文章

  • 【完结】LeetCode 热题 HOT 100分类+题解+代码详尽指南
    目录LeetCode热题100前言LeetcodeTop100题目和答案-哈希LeetcodeTop100题目和答案-双指针篇LeetcodeTop100题目和答案-滑动窗口篇LeetcodeTop100题目和答案-子串篇LeetcodeTop100题目和答案-普通数组篇LeetcodeTop100题目和答案-矩阵篇LeetcodeTop100题目和......
  • CentOS 乱码问题解决
    首先要区别3个概:编码集、字符集、字体是完全不同的东西,我们要解决的是字符集问题。当一个系统初始化完毕后,会生成一个/usr/lib/locale/locale-archive文件,这个是字符集二进制文件,是系统不同语言运行的核心,通过命令locale-a可以看到当前文件中支持的语言locale命令可以......
  • [题解] [ABC221H] Count Multiset - DP
    [ABC221H]CountMultiset题面翻译输入两个正整数\(N,M\),并存在一个集合,问你一个长度为\(k\)的合法集合存在多少个?你需要回答\(k\)的值为\(1\)到\(N\)的每种情况。一个合法的集合定义指长度为\(k\),元素和为\(N\),每一个数字在集合中出现的次数都小于等于\(M\)的集......
  • CF506D题解
    Mr.Kitayuta'sColorfulGraph算法:根号分治。题目大意先说一下:给一个\(n\)点\(m\)边的无向图,边有颜色。\(q\)组询问,每次给出\(u,v\),求有多少种颜色\(c\),使得存在一条\(u\)到\(v\)的路径,这个路径中每条边的颜色都为\(c\)。先考虑一个朴素的暴力,暴力对每个颜色加边,......
  • UVA12683 Odd and Even Zeroes 题解
    题目简述定义\(\operatorname{count}(num)\)表示\(num\)末尾\(0\)的个数。给出\(n\)(\(n\leq10^{18}\)),求\(\sum\limits_{i=0}^{n}[2\mid\operatorname{count}(i!)]\)。题目分析对于一个\(i\),以下记成\(n\)。\(n!\)末尾\(0\)的个数取决于\(1\simn\)......
  • [ARC080F] Prime Flip 题解
    Description有无限枚硬币,其中有\(n\)枚硬币\(x_{1\ldotsn}\)。初始时正面朝上,其余均为背面朝上,每次可以选择一段区间\([l,r]\),将区间内所有硬币翻转,其中\(r-l+1\)为一个奇质数。问最少多少次能将所有硬币全部翻为背面朝上。\(1\leqn\leq100,1\leqx_1\leqx_2\leq\ld......
  • CF369D Valera and Fools 题解
    传送门LuoguCodeforces题意简述有\(n\)个傻子智者站成一排,每人手中有\(k\)发子弹,每次每人会向除自己外编号最小的人开枪,第\(i\)个人开枪的命中率为\(p_i\%\),剩余最多一人时结束,问有多少种可能的局面。解法说明从题目要求中可以发现,每次一定是编号最小的人向编号第二......
  • 优化爬虫体验:揭秘IP重复率过高问题解决方案
    在当今信息爆炸的时代,网络中蕴藏着大量宝贵的数据,而爬虫技术成为我们提取这些数据的重要工具。然而,随着爬虫的广泛使用,IP重复率高的问题也随之而来。本篇博文将揭秘解决这一问题的关键方法——使用IP代理。一、IP高重复问题带来的挑战&nbsp;被封禁风险:当一个IP在短时间内频......
  • 优化爬虫体验:揭秘IP重复率过高问题解决方案
    在当今信息爆炸的时代,网络中蕴藏着大量宝贵的数据,而爬虫技术成为我们提取这些数据的重要工具。然而,随着爬虫的广泛使用,IP重复率高的问题也随之而来。本篇博文将揭秘解决这一问题的关键方法——使用IP代理。一、IP高重复问题带来的挑战&nbsp;被封禁风险:当一个IP在短时间内频......
  • 优化爬虫体验:揭秘IP重复率过高问题解决方案
    在当今信息爆炸的时代,网络中蕴藏着大量宝贵的数据,而爬虫技术成为我们提取这些数据的重要工具。然而,随着爬虫的广泛使用,IP重复率高的问题也随之而来。本篇博文将揭秘解决这一问题的关键方法——使用IP代理。一、IP高重复问题带来的挑战&nbsp;被封禁风险:当一个IP在短时间内频......