[ACNOI2022]抬头仰望卷爷的脚步

280 篇文章 1 订阅

题目

题目背景
所有人都在享受休闲时光,但 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(1in) 次插入节点 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 T5×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 vvn 的节点中,前缀非严格最大值。
  • 所有 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(ji),只要这个值在某范围内即可。于是问题转化为求解
k b   m o d   m ∈ [ L , R ] kb\bmod m\in[L,R] kbmodm[L,R]

的最小 k ∈ N k\in\N kN 。对于 k ∈ N + k\in\N^+ kN+ 可以转化为 ( 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 kN 问题。

形象化一点,等价于在长为 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<LR<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=bmbm,那么第二圈跳进来的位置就是 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=m1 时就会到达这个上界。究其原因,是因为 “跳一圈” 已经退化为跳一步。可以反过来考虑,相当于取相反数得到
− 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} bmb<2m 的情形。这样一来,模数的变化次数就是 O ( log ⁡ m ) \mathcal O(\log m) O(logm) 了!

回到原问题上。求出了 Δ = j − i \Delta=j{-}i Δ=ji 使得 v i → v j v_i\to v_j vivj,显然我们也可以再变化到 v j → v j + Δ → v j + 2 Δ v_j\to v_{j+\Delta}\to v_{j+2\Delta} vjvj+Δvj+,直到下标的范围或值的范围有误。可以发现这是一个取模的过程。取模的过程只会进行 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;
}

后记

也许几十年之后,我们的道德观念也会被推翻,就像我们批驳旧道德那样;我们的生命将化为浮尘;只有一样东西是不变的,那就是『真实』。

到了那天,正文将成为废纸,只有《题目背景》的价值永存。因为它就是那个不变的东西。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值