[THUPC2022初赛]最小公倍树

280 篇文章 1 订阅

题目

传送门 to LOJ

传送门 to luogu

思路

My vegetable died. \text{My vegetable died.} My vegetable died. 我菜死了。

Kruskal \text{Kruskal} Kruskal

比较自然的想法是枚举 lcm = n \text{lcm}=n lcm=n,显然只需要将 n n n 的因数之间连边。

但你很快会想到,其实 n n n 的因数大多已经相连。比如 n p n\over p pn n p 2 n\over p^2 p2n 。更进一步地,如果 gcd ⁡ ( x , y ) = d \gcd(x,y)=d gcd(x,y)=d 存在,那么 x , y x,y x,y 肯定已经相连了。

d d d 不存在,我就傻眼了。我想不到可以取出 最小的 d d d 的倍数 p d p_d pd 来接替这一使命。因为使得 ( x , p d ) (x,p_d) (x,pd) 连通的最大边权不超过 lcm ( x , p d ) ⩽ x p d d \text{lcm}(x,p_d)\leqslant \frac{xp_d}{d} lcm(x,pd)dxpd,同理 ( y , p d ) (y,p_d) (y,pd) 连通代价不超过 y p d d \frac{yp_d}{d} dypd 。若 p d < x < y p_d<x<y pd<x<y y p d d < x y d \frac{yp_d}{d}<\frac{xy}{d} dypd<dxy,故边 ⟨ x , y ⟩ \langle x,y\rangle x,y 永远不会被用到。

所以 x = p d x=p_d x=pd,即有用的边只有 ( p d , y )    ( d ∣ y ) (p_d,y)\;(d\mid y) (pd,y)(dy) 。这样的边只有 O ( n log ⁡ n ) \mathcal O(n\log n) O(nlogn) 条,调和级数。直接 Kruskal \text{Kruskal} Kruskal 复杂度 O ( n log ⁡ 2 n ) \mathcal O(n\log^2 n) O(nlog2n),其中 n = R − L n=R-L n=RL

Prim    o r    Boruvka \text{Prim}\;or\;\text{Boruvka} PrimorBoruvka

这种 M S T \rm MST MST 算法 支持枚举!尤其是 Boruvka \text{Boruvka} Boruvka,因为每轮 O ( n ) \mathcal O(n) O(n) 执行也是被允许的!

枚举啥呢?枚举 gcd ⁡ = d \gcd=d gcd=d 。那么只需要连向 d d d 的倍数中最小的一个(不在当前连通块内的)点。

如果是 Boruvka \text{Boruvka} Boruvka,处理出每个 d d d 的最小倍数和次小倍数(与最小不在同一连通块中)然后枚举每个点,单轮 O ( n log ⁡ n ) \mathcal O(n\log n) O(nlogn) 调和级数,总复杂度 O ( n log ⁡ 2 n ) \mathcal O(n\log^2 n) O(nlog2n)

如果是 Prim \text{Prim} Prim,对每个 d d d 都动态维护。那么总共会有 O ( n log ⁡ n ) \mathcal O(n\log n) O(nlogn) 次修改,每次修改使用任何 log ⁡ \log log 数据结构维护,复杂度 O ( n log ⁡ 2 n ) \mathcal O(n\log^2 n) O(nlog2n)

注意复杂度是关于 n = R − L n=R-L n=RL 的。规定 R ⩽ 1 0 6 R\leqslant 10^6 R106 或许是为了使答案不超过 2 63 2^{63} 263

代码

#include <cstdio> // I am the believer of XJX
#include <iostream> // Almighty XJX yyds!!!
#include <algorithm> // oracle: ZXY yydBUS!!!
#include <cstring> // Huge EggDog devoured me!!!
#include <cctype> // decent XYX yydLONELY!!!
using llong = long long;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
# define rep0(i,a,b) for(int i=(a); i!=(b); ++i)
inline int readint(){
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar()) if(c == '-') f = -f;
	for(; isdigit(c); c=getchar()) a = a*10+(c^48);
	return a*f;
}
inline llong getLcm(int a, int b){
	return llong(a)/std::__gcd(a,b)*b;
}

const int MAXN = 100005, MAXM = 20*MAXN;
struct Edge { int a, b; llong c; };
Edge e[MAXM]; int tot;

int fa[MAXN];
int find(int a){
	if(fa[a] == a) return a;
	return fa[a] = find(fa[a]);
}

bool cmp(const Edge &x, const Edge &y){
	return x.c < y.c;
}
int main(){
	int l = readint(), r = readint();
	rep(d,1,r-l){ // multiple
		int p = ((l-1)/d+1)*d;
		for(int i=p+d; i<=r; i+=d)
			e[++ tot] = Edge{p,i,getLcm(i,p)};
	}
	std::sort(e+1,e+tot+1,cmp);
	llong ans = 0; rep(i,1,r-l+1) fa[i] = i;
	rep(i,1,tot) if(find(e[i].a-l+1) != find(e[i].b-l+1))
		fa[fa[e[i].a-l+1]] = fa[e[i].b-l+1], ans += e[i].c;
	printf("%lld\n",ans);
	return 0;
}

后记

d u D C u t \sf{duDCut} duDCut 在赛上看了一眼就秒切了。牠本想找我讨论一下,但它显然没想过,我根本无法与它相提并论。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]和引用\[2\]的代码,可以看出这是两个关于博弈问题的代码。引用\[1\]是关于欺诈游戏(博弈和概率/纳什均衡)的代码,而引用\[2\]是关于大富翁(上博弈)的代码。 在大富翁问题中,每个玩家在一个形结构的游戏中选择一个节点,并获得该节点及其子节点的价值之和。每个玩家的目标是最大化自己的总价值。代码中使用了动态规划的思想,通过维护前缀和和前缀已经取了多少个数来计算每个数的贡献。 在欺诈游戏中,有两个玩家进行游戏,每个玩家轮流选择一个数,并获得该数及其后面的所有数的负值之和。每个玩家的目标是最大化自己的总得分。代码中使用了深度优先搜索来计算每个节点的得分,并通过排序和选择奇数位置的数来计算最终的得分。 综上所述,引用\[1\]是关于欺诈游戏(博弈和概率/纳什均衡)的代码,而引用\[2\]是关于大富翁(上博弈)的代码。 #### 引用[.reference_title] - *1* *2* [清华大学学生程序设计竞赛暨高校邀请赛(THUPC)2023 - 初赛(待补题)](https://blog.csdn.net/Code92007/article/details/129484118)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值