[CSP-S模拟测试]:小Y的图(最小生成树+LCA)

题目传送门(内部题131)


输入格式

  第一行三个整数$n$、$m$和$Q$。
  接下来$m$行每行三个整数$x$、$y$、$z$($1\leqslant x,y\leqslant n,1\leqslant z\leqslant 1,000,000$),表示有一条连接$x$和$y$长度为$z$的边。
  接下来$Q$行每行两个整数$x$、$y$($x\neq y$),表示一组询问。


输出格式

  $Q$行每行一个整数,表示一组询问的答案。


样例

样例输入:

5 5 4
1 2 3
1 3 2
3 2 1
1 4 5
2 4 4
1 2
1 4
3 5
2 4

样例输出:

2
4
-1
4


数据范围与提示

  对于前$30\%$的测试数据,满足$1\leqslant n,m,Q\leqslant 1,000$。
  对于另外$30\%$的测试数据,保证图联通。
  对于$100\%$的测试数据,满足$1\leqslant n,m,Q\leqslant 300,000$。
  对于$100\%$的测试数据,保证不存在自环,但可能存在重边。
  请使用$scanf,printf$或速度更快的读入输出方式。


题解

有人问我$30\%$的暴力怎么打(问题是$ta$还$A$了)……

那我就简单说一下。

最短路思想,用$Dijkstra$,将原本的$dis[v]=dis[u]+e[i].w$改成$dis[v]=\max(dis[u],e[i].w)$就好了。

千万不要想当然,比方说下面这份代码$\downarrow$

认真看一下,虽说时间复杂度是对的,但是如果如下面这张图$\downarrow$

我们可能会选择$x\stackrel{2}{\rightarrow}o\stackrel{1}{\rightarrow}y$这条路径;然而当发现$x\stackrel{1}{\rightarrow}o$更优时会发现$o\rightarrow y$已经走过了,就不会再更新答案,这也就是为什么最短路不是这么求。

现在来说正解吧,先来考虑联通的情况。

这个最优路径上的所有边一定位于最小生成树上,所以可以求$x,y$到$lca$上的最长边即可。

不联通的情况也无非就是记录一下两个点在不在一个联通块内即可。

时间复杂度:$\Theta(m\log m+q\log n)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
struct node{int x,y,z;bool d;}b[300001];
struct rec{int nxt,to,w;}e[600001];
int head[300001],cnt,tot;
int n,m,Q;
int f[300001],depth[300001],bel[300001],fa[300001][21],mi[300001][21];
bool cmp(node a,node b){return a.z<b.z;}
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
void add(int x,int y,int w)
{
	e[++cnt].nxt=head[x];
	e[cnt].to=y;
	e[cnt].w=w;
	head[x]=cnt;
}
void dfs(int x)
{
	bel[x]=tot;
	for(int i=head[x];i;i=e[i].nxt)
	{
		if(depth[e[i].to])continue;
		depth[e[i].to]=depth[x]+1;
		fa[e[i].to][0]=x;
		mi[e[i].to][0]=e[i].w;
		for(int j=1;j<=20;j++)
		{
			fa[e[i].to][j]=fa[fa[e[i].to][j-1]][j-1];
			mi[e[i].to][j]=max(mi[e[i].to][j-1],mi[fa[e[i].to][j-1]][j-1]);
		}
		dfs(e[i].to);
	}
}
int LCA(int x,int y)
{
	if(depth[x]>depth[y])swap(x,y);
	int res=0;
	for(int i=20;i>=0;i--)
		if(depth[fa[y][i]]>=depth[x])
		{
			res=max(res,mi[y][i]);
			y=fa[y][i];
		}
	if(x==y)return res;
	for(int i=20;i>=0;i--)
		if(fa[x][i]!=fa[y][i])
		{
			res=max(res,max(mi[x][i],mi[y][i]));
			x=fa[x][i];y=fa[y][i];
		}
	return max(res,max(mi[x][0],mi[y][0]));
}
int main()
{
	scanf("%d%d%d",&n,&m,&Q);
	for(int i=1;i<=n;i++)f[i]=i;
	for(int i=1;i<=m;i++)scanf("%d%d%d",&b[i].x,&b[i].y,&b[i].z);
	sort(b+1,b+m+1,cmp);
	for(int i=1;i<=m;i++)
	{
		int x=find(b[i].x);
		int y=find(b[i].y);
		if(x==y)continue;
		b[i].d=1;
		f[y]=x;
	}
	for(int i=1;i<=m;i++)
		if(b[i].d)
		{
			add(b[i].x,b[i].y,b[i].z);
			add(b[i].y,b[i].x,b[i].z);
		}
	for(int i=1;i<=n;i++)
		if(!depth[i])
		{
			tot++;
			depth[i]=1;
			dfs(i);
		}
	while(Q--)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		if(bel[x]!=bel[y])puts("-1");
		else printf("%d\n",LCA(x,y));
	}
	return 0;
}


rp++

原文地址:https://www.cnblogs.com/wzc521/p/11824129.html

时间: 2024-11-11 18:26:44

[CSP-S模拟测试]:小Y的图(最小生成树+LCA)的相关文章

[CSP-S模拟测试]:小P的生成树(数学+Kruskal)

题目描述 小$P$是个勤于思考的好孩子,自从学习了最大生成树后,他就一直在想:能否将边权范围从实数推广到复数呢?可是马上小$P$就发现了问题,复数之间的大小关系并没有定义.于是对于任意两个复数$z_1,z_2$,小$P$定义$z_1<z_2$当且仅当$|z_1|<|z_2|$. 现在,给出一张$n$个点$m$条边的简单无向带权图,小$P$想问你,如果按照他对复数大小的定义,这个图的最大生成树是什么? 输入格式 输入的第一行为两个正整数$n$和$m$,分别表示这个无向图的点数和边数. 接下来$m

[CSP-S模拟测试]:小盆友的游戏(数学 or 找规律)

题目传送门(内部题110) 输入格式 第一行一个整数$N$,表示小盆友的个数. 第二行$N$个整数$A_i$,如果$A_i=-1$表示$i$目前是自由身,否则$i$是$A_i$的跟班. 输出格式 一个整数$X$,表示在模$10^9+7$的情况下,期望总猜拳次数. 样例 样例输入1: 2-1 -1 样例输出1: 1 样例输入2: 3-1 -1 -1 样例输出2: 3 样例输入3: 4-1 -1 -1 -1 样例输出3: 7 样例输入4: 5-1 -1 -1 -1 -1 样例输出4: 15 样例输入

[CSP-S模拟测试]:小P的单调数列(树状数组+DP)

题目描述 小$P$最近喜欢上了单调数列,他觉得单调的数列具有非常多优美的性质.经过小$P$复杂的数学推导,他计算出了一个单调增数列的艺术价值等于该数列中所有书的总和.并且以这个为基础,小$P$还可以求出任意一个数列的艺术价值,它等于将这个数列顺次划分若干个极长单调区间(相邻两个单调区间的单调性必须不相同)后,每个单调区间中元素总和的平均值.比如对于数列$3\ 7\ 9\ 2\ 4\ 5$,它将被划分为$[3\ 7\ 9]\ [2]\ [4\ 5]$,其艺术价值为$\frac{19+2+9}{3}

[CSP-S模拟测试]:小W的魔术(数学 or 找规律)

题目传送门(内部题130) 输入格式 第一行一个整数$n$,表示字符串的长度. 第二行一个只包含小写字母的字符串$s$. 输出格式 一行一个整数表示答案对$998244353$取模后的结果. 样例 样例输入: 2a 样例输出: 625 数据范围与提示 对于$20\%$的测试数据,$n\leqslant 4$. 对于$40\%$的测试数据,$n\leqslant 100$. 对于$60\%$的测试数据,$n\leqslant 10,000,000$. 对于另外$20\%$的测试数据,保证$s$只由

[CSP-S模拟测试]:小L的数(数位DP+模拟)

题目传送门(内部题132) 输入格式 第一行一个整数$t$. 接下来$t$行每行一个整数$n$. 输出格式 $t$行,每行一个整数表示答案. 样例 样例输入: 418182312326910521093203 样例输出: 1233 数据范围与提示 对于前$5\%$的测试数据,满足答案小于等于$1$. 对于前$20\%$的测试数据,满足答案小于等于$2$. 对于前$40\%$的测试数据,满足$n\leqslant 300,000$. 对于前$60\%$的测试数据,满足答案小于等于$3$,$n\le

2018冬令营模拟测试赛(三)

2018冬令营模拟测试赛(三) [Problem A]摧毁图状树 试题描述 输入 见"试题描述" 输出 见"试题描述" 输入示例 见"试题描述" 输出示例 见"试题描述" 数据规模及约定 见"试题描述" 题解 这题没想到贪心 QwQ,那就没戏了-- 贪心就是每次选择一个最深的且没有被覆盖的点向上覆盖 \(k\) 层,因为这个"最深的没有被覆盖的点"不可能再有其它点引出的链覆盖它了,而它又

2018冬令营模拟测试赛(五)

2018冬令营模拟测试赛(五) [Problem A][UOJ#154]列队 试题描述 picks 博士通过实验成功地得到了排列 \(A\),并根据这个回到了正确的过去.他在金星凌日之前顺利地与丘比签订了契约,成为了一名马猴烧酒. picks 博士可以使用魔法召唤很多很多的猴子与他一起战斗,但是当猴子的数目 \(n\) 太大的时候,训练猴子就变成了一个繁重的任务. 历经千辛万苦,猴子们终于学会了按照顺序排成一排.为了进一步训练,picks 博士打算设定一系列的指令,每一条指令 \(i\) 的效果

模拟测试(vj)

做这份模拟测试,已经崩溃了,英文看不懂,题意理解错.到结束了只a了第一题,人生陷入了低谷,于是花了一天的时间终于把不会的弄明白了,在这里写一份总结~ T1,简单的模拟,如果打枪打中一支鸟,将这个位置设为0,并向两边扩散,注意这个位置一定要有鸟. 代码~ #include<bits/stdc++.h> using namespace std; int a[30000]; int n,m; int main() { cin>>n; for(int i=1;i<=n;i++) ci

2018冬令营模拟测试赛(十九)

2018冬令营模拟测试赛(十九) [Problem A]小Y 试题描述 输入 见"试题描述" 输出 见"试题描述" 输入示例 见"试题描述" 输出示例 见"试题描述" 数据规模及约定 见"试题描述" 题解 目前未知. 这题目前就能做到 \(O(n \sqrt{M} \log n)\),其中 \(M\) 是逆序对数,然而会被卡 \(T\):当然这题暴力可以拿到和左边那个算法一样的分数,只要暴力加一个剪枝:当左