[COCI2017-2018#5] Pictionary

博客详细介绍了COCI2017-2018赛季第五场的一个题目,涉及点的连通性问题。作者探讨了如何将gcd(a,b)=k的点转化为所有k的倍数点连边,并通过货车运输问题的思路,利用最小生成树和倍增算法解决求两点最早联通时间的问题。文章提供了解题方案和代码实现。" 104872117,9198068,JavaScript操作元素属性技巧,"['JavaScript', 'DOM操作', '属性操作']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

题目大意

给定 n n n 个点,第 i i i 次会把所有编号满足 gcd ⁡ ( a , b ) = m − i + 1 \gcd(a,b)=m-i+1 gcd(a,b)=mi+1 的点对连边,对于每个询问 a , b a,b a,b,求点 a a a 和点 b b b 什么时候联通。

Solution

场上没做出来,还是逊了。

脑洞不够,套路不够。其实想通了还是蛮简单的。

首先我们考虑把连边转化一下。如果 gcd ⁡ ( a , b ) = k \gcd(a,b)=k gcd(a,b)=k,那么显然 a a a b b b 都是 k k k 的倍数。而每一次把 gcd ⁡ ( a , b ) = k \gcd(a,b)=k gcd(a,b)=k 的所有点连边,最后的连通性与把所有的 k k k 的倍数是一样一样的。因此我们可以说,第 i i i 次把所有编号为 m − i + 1 m-i+1 mi+1 的倍数的点之间连边。

然后我们可以发现,如果我们把每次加的边编号的话,那么两个点之间最早的联通时间是路径上的最大值。因此就转化为求两点间所有路径上的最大值最小是多少。

于是自然联想到 货车运输 这题。考虑每次按序加边,同时维护是一颗树。当然可以选择克鲁斯卡尔重构树,或者直接最小生成树都是可以的,我代码中给出克鲁斯卡尔重构树的写法。

还有一个就是路径最值,可以倍增维护,当然也可以用树剖(不写挂的话)。

Code

#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
using namespace std;
const int MAXN=2e5+10;
int f[MAXN],val[MAXN];
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
vector<int> e[MAXN];
int fa[MAXN][20],dp[MAXN][20],dep[MAXN];
void dfs(int x,int fat){
	fa[x][0]=fat;dp[x][0]=val[fat];
	for(int i=1;i<20;i++)
		fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int i=1;i<20;i++)
		dp[x][i]=max(dp[x][i-1],dp[fa[x][i-1]][i-1]);
	for(int i=0;i<e[x].size();i++){
		int s=e[x][i];if(s==fat) continue;
		dep[s]=dep[x]+1;dfs(s,x);
	}
}
int LCA(int x,int y){
	if(dep[x]<dep[y]) swap(x,y);
	int dlt=dep[x]-dep[y];
	for(int i=0;i<20;i++)
		if(dlt&(1<<i))
			x=fa[x][i];
	if(x==y) return x;
	for(int i=19;i>=0;i--)
		if(fa[x][i]!=fa[y][i])
			x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}
int main()
{
	int n,m,Q;
	scanf("%d%d%d",&n,&m,&Q);
	int cnt=n;
	for(int i=1;i<=n*2;i++)
		f[i]=i;
	for(int i=1;i<=m;i++){
		int dis=m-i+1;
		for(int j=dis;j+dis<=n;j+=dis){
			int u=find(j),v=find(j+dis);
			if(u==v) continue;
			val[++cnt]=i;
			f[u]=f[v]=cnt;
			e[cnt].push_back(u);
			e[cnt].push_back(v);
		}
	}
	dfs(cnt,0);
	int x,y;
	while(Q--){
		scanf("%d%d",&x,&y);
		int lca=LCA(x,y);
		int dlt=dep[x]-dep[lca];
		int ans=0;
		for(int i=0;i<19;i++)
			if(dlt&(1<<i))
				ans=max(ans,dp[x][i]),
				x=fa[x][i];
		dlt=dep[y]-dep[lca];
		for(int i=0;i<19;i++)
			if(dlt&(1<<i))
				ans=max(ans,dp[y][i]),
				y=fa[y][i];
		printf("%d\n",ans);
	}
}

End

校内模拟赛 T3,受益匪浅。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值