一个很自然的想法是,由于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⌋
若 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
不在话下
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的