Description
Analysis
一个很自然的想法是,由于k很大,我们二分一个分数,统计网格有多少个比它大
先不考虑如何二分分数,假装我们已经得到了分数
a
b
\dfrac{a}{b}
ba,如何统计比它大的个数呢?直线上方整点个数,妈妈我会类欧
类欧
我们要求的是这个 f ( a , b , c , n ) = ∑ i = 0 n ⌊ a i + b c ⌋ f(a,b,c,n)=\sum_{i=0}^n \lfloor \dfrac{ai+b}{c}\rfloor f(a,b,c,n)=∑i=0n⌊cai+b⌋
Case 1
若
a
≥
c
a\geq c
a≥c或
b
≥
c
b \geq c
b≥c显然可以分离出常数项来,这是trivial的,此时
f
(
a
,
b
,
c
,
n
)
=
f
(
a
%
c
,
b
%
c
,
c
,
n
)
+
n
(
n
+
1
)
a
2
c
+
(
n
+
1
)
b
c
f(a,b,c,n)=f(a \% c,b\%c,c,n)+\dfrac{n(n+1)a}{2c}+\dfrac{(n+1)b}{c}
f(a,b,c,n)=f(a%c,b%c,c,n)+2cn(n+1)a+c(n+1)b
不在话下
Case 2
a
<
c
a<c
a<c且
b
<
c
b<c
b<c,把上面那条搬下来,搞一波事情
f
(
a
,
b
,
c
,
n
)
=
∑
i
=
0
n
⌊
a
i
+
b
c
⌋
f(a,b,c,n)=\sum_{i=0}^n \lfloor \dfrac{ai+b}{c}\rfloor
f(a,b,c,n)=∑i=0n⌊cai+b⌋
魔幻的一步:令
m
=
⌊
a
n
+
b
c
⌋
m=\lfloor \dfrac{an+b}{c}\rfloor
m=⌊can+b⌋
则
f
(
a
,
b
,
c
,
n
)
=
∑
i
=
0
n
∑
j
=
1
m
[
a
i
+
b
c
≥
j
]
f(a,b,c,n)=\sum_{i=0}^n \sum_{j=1}^m [\dfrac{ai+b}{c} \geq j]
f(a,b,c,n)=∑i=0n∑j=1m[cai+b≥j]
=
∑
i
=
0
n
∑
j
=
0
m
−
1
[
a
i
≥
j
c
+
c
−
b
]
=\sum_{i=0}^n \sum_{j=0}^{m-1} [ai \geq jc+c-b]
=∑i=0n∑j=0m−1[ai≥jc+c−b]
=
∑
i
=
0
n
∑
j
=
0
m
−
1
[
a
i
>
j
c
+
c
−
b
−
1
]
=\sum_{i=0}^n \sum_{j=0}^{m-1} [ai > jc+c-b-1]
=∑i=0n∑j=0m−1[ai>jc+c−b−1]
=
∑
i
=
0
n
∑
j
=
0
m
−
1
[
i
>
j
c
+
c
−
b
−
1
a
]
=\sum_{i=0}^n \sum_{j=0}^{m-1} [i > \dfrac{jc+c-b-1}{a}]
=∑i=0n∑j=0m−1[i>ajc+c−b−1]
左边只与
i
i
i有关,爽了
=
∑
j
=
0
m
−
1
n
−
⌊
j
c
+
c
−
b
−
1
a
⌋
=\sum_{j=0}^{m-1} n-\lfloor\dfrac{jc+c-b-1}{a}\rfloor
=∑j=0m−1n−⌊ajc+c−b−1⌋
=
n
m
−
f
(
c
,
c
−
b
−
1
,
a
,
m
−
1
)
=nm-f(c,c-b-1,a,m-1)
=nm−f(c,c−b−1,a,m−1)
我们发现第一、三个参数在模了之后调换了位置,这类似于欧几里得算法,并且由欧几里得算法知,这么迭代下去复杂度也是一个log
代码
ll f(ll a,ll b,ll c,ll n)//sigma (ai+b)/c
{
if(a==0) return n*(b/c);
if(a>=c || b>=c) return f(a%c,b%c,c,n)+n*(n+1)/2*(a/c)+(n+1)*(b/c);
ll m=(a*n+b)/c;
return n*m-f(c,c-b-1,a,m-1);
}
二分
然而二分分数是个棘手的问题(听说可以二分实数强转但具体不清楚)
今年WC上讲了一个叫傻逼树(Stern Brocot Tree)的东西来生成所有既约分数的姿势,详见scape鸽鸽的课件
在这棵树上遍历就相当于二分过程啦><
然而并不能一步步走qwq,注意到树的深度是O(n)的,但是这棵树一个性质是,从根走到任意一个分数,拐弯的次数不超过Log次
那么在走的时候二分一下拐点就可以了
单次查询就是3个log的