Sirni(最小生成树,埃氏筛)

7 篇文章 0 订阅
1 篇文章 0 订阅

题目大意

给你 n n n 个点,每个点有一权值 P i P_i Pi , i i i, j j j两个点连边的花费是 m i n min min( P i P_i Pi% P j P_j Pj , P j P_j Pj% P i P_i Pi)
求最小生成树。
数据范围:
1 ≤ n ≤ 1 0 5 , 1 ≤ P i ≤ 1 0 7 1\le n\le 10^5,1\le P_i\le 10^7 1n105,1Pi107

思路

首先30分我们可以通过枚举两两连边后用 K r u s k a l Kruskal Kruskal 做,复杂度 O ( n 2 l o g n 2 ) O(n^2log_{n^2}) O(n2logn2)
其实我们排序时还可以用类似桶排序的方法用vector或者前向星存储边访问,但是还是会超时,却给我们正解提供思路
然后考虑正解,
我们记值域为 T=[1, 1 0 7 10^7 107]
我们假设两个点 a , b a,b a,b 可以发现若 P a ≤ P b P_a\le P_b PaPb那么取 m i n min min 时只可能取 P b % P a P_b\% P_a Pb%Pa
那我们如何决策呢
首先我们可以将所有 P b = k P a P_b=kP_a Pb=kPa的点缩成一个点,保存最小的 P a P_a Pa ,因为他们相连的代价为0,我们可以用类似埃氏筛的方法处理
然后场上所有 P a , P b P_a,P_b Pa,Pb都是不同的且不存在倍数关系
我们发现,算法瓶颈在于边太多了,于是考虑减少边的数量(即可以判断一些不会选的边提前去除)
怎么做呢
假设有三个点 a , b , c a,b,c a,b,c 满足 P a &lt; P b &lt; P c P_a&lt;P_b&lt;P_c Pa<Pb<Pc 并且 P b = k ∗ P a + s , P c = k ∗ P a + t ( 0 &lt; s , t &lt; P a ) P_b=k*P_a+s,P_c=k*P_a+t(0&lt;s,t&lt;P_a) Pb=kPa+s,Pc=kPa+t(0<s,t<Pa)
a , b a,b a,b 连边代价为 s s s ; a , c a,c a,c连边代价为 t t t ; b , c b,c b,c连边代价 q q q 虽然不知道是多少但有一个范围即 : 0 ≤ q ≤ t − s 0\le q\le t-s 0qts
给这三个点连边只有三种情况:
1. a − b − c 1. a-b-c 1.abc
代价 w 1 w_1 w1: s + q s+q s+q, s ≤ w 1 ≤ t s\le w_1\le t sw1t
2. b − a − c 2. b-a-c 2.bac
代价 w 2 w_2 w2: s + t s+t s+t, w 2 = s + t w_2=s+t w2=s+t
3. a − c − b 3. a-c-b 3.acb
代价 w 3 w_3 w3: t + q t+q t+q, t ≤ w 3 ≤ 2 ∗ t − s t\le w_3\le 2*t-s tw32ts
于是 w 1 &lt; m i n ( w 2 , w 3 ) w_1&lt;min(w_2,w_3) w1<min(w2,w3),k一定时,对于 a a a, a b ab ab连边是最优的, b-c这条边我们会在对于 b b b 考虑时决策
我们记 f ( i , k ) f(i,k) f(i,k) 表示
那我们发现就只把 k k k 枚举一遍找到最小一个满足 P x = k ∗ P a + s ( 0 &lt; s &lt; P a ) P_x=k*P_a+s(0&lt;s&lt;P_a) Px=kPa+s(0<s<Pa) P x P_x Px, i , x i,x i,x连边即可

那么问题转化为 将 P P P数组在值域为[1, 1 0 7 10^7 107]中bool标记,后对于一个值 x x x 快速找到在它右侧第一个出现的值,那么从右至左简单的 D p Dp Dp扫一遍即可
后面用 30分的第2种做法即可
分析一下,由于 P i P_i Pi互不相同,那么枚举 P i P_i Pi的倍数时我们的边上限为 T l o g l o g n Tloglogn Tloglogn 类似埃氏筛的时间复杂度,于是桶排序访问时间复杂度为 O ( n + T ) O(n+T) O(n+T)
那么总的时间复杂度为 O ( T l o g l o g n ) O(Tloglogn) O(Tloglogn)

后记

如有不懂可在评论区留言,万分感谢点赞的同学们!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值