题目
题目背景
所有人都在享受休闲时光,但
O
U
Y
E
\sf OUYE
OUYE 悄悄地将题全部卷完,到达无人可及之境,渴望自己的孤独!
“确实,在 O I \rm OI OI 世界,不能完成题目的人被称为废物。但是抛弃同伴的人——”
“成了我卷爷。” O U Y E \sf OUYE OUYE 把话头接了过去。“对了,你还没送我进入国集的贺礼吧?”
题目描述
有一棵
B
S
T
\rm BST
BST,最初为空。第
i
(
1
⩽
i
⩽
n
)
i\;(1\leqslant i\leqslant n)
i(1⩽i⩽n) 次插入节点
i
i
i,其键值为
(
a
+
i
b
)
m
o
d
m
(a+ib)\bmod m
(a+ib)modm 。插入规则为,左子树的所有节点的键值应严格小于当且节点。
求节点 n n n 的深度,即到节点 1 1 1 的边数。
数据范围与约定
数据组数
T
⩽
5
×
1
0
4
T\leqslant 5\times 10^4
T⩽5×104,满足
max
(
n
,
m
)
⩽
1
0
9
\max(n,m)\leqslant 10^{9}
max(n,m)⩽109 。
思路
考虑计算贡献。不难发现,键值为 v i v_i vi 的 i i i 号节点是 n n n 的祖先,有两种:
- 所有 v ⩽ v n v\leqslant v_n v⩽vn 的节点中,前缀非严格最大值。
- 所有 v n < v v_n<v vn<v 的节点中,前缀严格最小值。
怎样找到这些点?考虑在某个
v
i
v_i
vi 处,立刻找到下一个
v
j
v_j
vj 。二者的差是
b
(
j
−
i
)
b(j{-}i)
b(j−i),只要这个值在某范围内即可。于是问题转化为求解
k
b
m
o
d
m
∈
[
L
,
R
]
kb\bmod m\in[L,R]
kbmodm∈[L,R]
的最小 k ∈ N k\in\N k∈N 。对于 k ∈ N + k\in\N^+ k∈N+ 可以转化为 ( k + 1 ) b m o d m ∈ [ L , R ] (k{+1})b\bmod m\in[L,R] (k+1)bmodm∈[L,R] 的 k ∈ N k\in\N k∈N 问题。
形象化一点,等价于在长为 m m m 的环上每次跳 b b b 步,问何时跳进某个区间内。若 [ L , R ] [L,R] [L,R] 内存在 b b b 的倍数,直接返回之。否则只需考虑 0 < L ⩽ R < b 0<L\leqslant R<b 0<L⩽R<b 的情形。
第一圈,我们跳不进去。第二圈呢?发现每圈只会跳进
[
0
,
b
)
[0,b)
[0,b) 一次。那么只要跳进来的这一次恰好是
[
L
,
R
]
[L,R]
[L,R] 内即可。设第一圈跳进来的位置是
t
=
⌈
m
b
⌉
b
−
m
t=\lceil{m\over b}\rceil b-m
t=⌈bm⌉b−m,那么第二圈跳进来的位置就是
2
t
m
o
d
b
2t\bmod b
2tmodb,以此类推到
k
t
m
o
d
b
kt\bmod b
ktmodb 。于是我们惊讶地发现,问题变为求解
k
t
m
o
d
b
∈
[
L
,
R
]
kt\bmod b\in[L,R]
ktmodb∈[L,R]
这怎么可能?再考察一下,发现
t
m
o
d
b
=
−
m
t\bmod b=-m
tmodb=−m 。联想到取模的本质是
k
b
−
λ
m
∈
[
L
,
R
]
kb-\lambda m\in[L,R]
kb−λm∈[L,R]
相当于变换了自变量,求解 λ \lambda λ 罢了!所以我们确实可能将模数变化为 b b b 。
可是递归的复杂度仍为
O
(
m
)
\mathcal O(m)
O(m) 。在
b
=
m
−
1
b=m{-}1
b=m−1 时就会到达这个上界。究其原因,是因为 “跳一圈” 已经退化为跳一步。可以反过来考虑,相当于取相反数得到
−
k
b
m
o
d
m
∈
[
−
R
,
−
L
]
-kb\bmod m\in[-R,-L]
−kbmodm∈[−R,−L]
所以在 b > m 2 b>{m\over 2} b>2m 时,就可以转化为 − b ≡ m − b < m 2 -b\equiv m{-}b<\frac{m}{2} −b≡m−b<2m 的情形。这样一来,模数的变化次数就是 O ( log m ) \mathcal O(\log m) O(logm) 了!
回到原问题上。求出了 Δ = j − i \Delta=j{-}i Δ=j−i 使得 v i → v j v_i\to v_j vi→vj,显然我们也可以再变化到 v j → v j + Δ → v j + 2 Δ v_j\to v_{j+\Delta}\to v_{j+2\Delta} vj→vj+Δ→vj+2Δ,直到下标的范围或值的范围有误。可以发现这是一个取模的过程。取模的过程只会进行 O ( log m ) \mathcal O(\log m) O(logm) 次,故总复杂度 O ( T log 2 m ) \mathcal O(T\log^2 m) O(Tlog2m) 。
代码
实现时可以分开考虑 v < v n v<v_n v<vn 和 v = v n v=v_n v=vn 的情形,因为 v = v n v=v_n v=vn 是容易计算的(它相当于 k b m o d m = 0 kb\bmod m=0 kbmodm=0 的周期)。
#include <cstdio> // megalomaniac JZM yydJUNK!!!
#include <iostream> // Almighty XJX yyds!!!
#include <algorithm> // XYX yydLONELY!!!
#include <cstring> // XEZ yydSON & SY yydMT!!!
#include <cctype> // oracle: ZXY yydBUS!!!
typedef long long llong;
# 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 void getMax(int &x, const int &y){
if(x < y) x = y;
}
inline int getGcd(int a, int b){
for(; b; a%=b,a^=b^=a^=b){}; return a;
}
const int INF = 0x3fffffff;
llong _find_it(int b, int m, int L, int R){
if(!L) return 0; // no matter what
if(b > m-b) return _find_it(m-b,m,m-R,m-L);
int res = (L-1)/b; L -= res*b, R -= res*b;
if(b <= R) return res+1; // 0 < L < b
return (R+_find_it(b-m%b,b,L,R)*m)/b+res;
}
int gcd; ///< ain't change
int find_it(int b, int m, int L, int R){
if(L <= 0 && 0 <= R) return 0;
if((L-1)/gcd >= R/gcd) return -1;
return int(_find_it(b,m,L,R));
}
int a, b, m;
/// @param n the upper bound (INCLUSIVE) of indice
llong calc_less(const int &vn, const llong &n){
llong pos = find_it(m-b,m,a-vn,a);
int nowv = int((a+pos*b)%m);
if(!(~pos) || nowv == vn) return 0; // dead
llong res = 1; // open range
for(llong d; true; ){
if((d = find_it(b,m,1,vn-nowv)) == -1) return res;
int mov = int(d*b%m), k = (vn-nowv-1)/mov;
if(k*d+pos > n) k = int((n-pos)/d);
if(k == 0) return res; // no more
pos += k*d, nowv += mov*k, res += k;
}
}
llong calc_greater(const int &vn, const llong &n){
llong pos = find_it(b,m,vn+1-a,m-1-a);
if(pos == -1 || pos > n) return 0; // dead
llong res = 1, d; // acceptable
for(int nowv=int((a+pos*b)%m),k; true; ){
d = find_it(m-b,m,1,nowv-vn-1);
if(!(~d)) return res;
int mov = m-int(d*b%m); // shift
k = (nowv-vn-1)/mov; // do not cross
if(k*d+pos > n) k = (n-pos)/d;
if(k == 0) return res; // no more
pos += k*d, nowv -= mov*k, res += k;
}
}
int main(){
for(int T=readint(); T; --T){
a = readint(), b = readint(), m = readint();
llong n; scanf("%lld",&n); a %= m, b %= m;
-- n; if((a += b) >= m) a -= m; // [0,n]
gcd = getGcd(b,m); int vn = int((a+(n%m)*b)%m);
llong ans = calc_less(vn,n)+calc_greater(vn,n);
ans += n/(m/gcd); // just the same
printf("%lld\n",ans);
}
return 0;
}
后记
也许几十年之后,我们的道德观念也会被推翻,就像我们批驳旧道德那样;我们的生命将化为浮尘;只有一样东西是不变的,那就是『真实』。
到了那天,正文将成为废纸,只有《题目背景》的价值永存。因为它就是那个不变的东西。