题目大意
给你
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
1≤n≤105,1≤Pi≤107
思路
首先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
Pa≤Pb那么取
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
<
P
b
<
P
c
P_a<P_b<P_c
Pa<Pb<Pc 并且
P
b
=
k
∗
P
a
+
s
,
P
c
=
k
∗
P
a
+
t
(
0
<
s
,
t
<
P
a
)
P_b=k*P_a+s,P_c=k*P_a+t(0<s,t<P_a)
Pb=k∗Pa+s,Pc=k∗Pa+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
0≤q≤t−s
给这三个点连边只有三种情况:
1.
a
−
b
−
c
1. a-b-c
1.a−b−c
代价
w
1
w_1
w1:
s
+
q
s+q
s+q,
s
≤
w
1
≤
t
s\le w_1\le t
s≤w1≤t
2.
b
−
a
−
c
2. b-a-c
2.b−a−c
代价
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.a−c−b
代价
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
t≤w3≤2∗t−s
于是
w
1
<
m
i
n
(
w
2
,
w
3
)
w_1<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
<
s
<
P
a
)
P_x=k*P_a+s(0<s<P_a)
Px=k∗Pa+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)、
后记
如有不懂可在评论区留言,万分感谢点赞的同学们!