problem
给定一个长度为 nnn 的排列 aaa。有 qqq 个询问,每次询问一个区间 [l,r][l,r][l,r]。求这个区间的 kkk 值。
其中 k=∑i=lr∑j=i+1rf(ai,aj),f(x,y)k=\sum_{i=l}^r\sum_{j=i+1}^rf(a_i,a_j),f(x,y)k=∑i=lr∑j=i+1rf(ai,aj),f(x,y) 为一个递归函数,定义如下:
- 当 x=yx=yx=y,其值为 111。
- 当 n≠yn\ne yn=y,其值为 f(x+y+∣x−y∣,∣x−y∣)f(x+y+|x-y|,|x-y|)f(x+y+∣x−y∣,∣x−y∣)。
- 若无限循环,其值为 000。
n,q≤1e5n,q\le 1e5n,q≤1e5。
solution
observation1:\text{observation1}:observation1: 首先观察这个函数,不难发现这个函数 x,yx,yx,y 的和是不变的。
所以当这个和为奇数的时候,一定会死循环,不可能走到 x=yx=yx=y 的局面。
否则 x,yx,yx,y 一定同奇偶。
observation2:\text{observation2}:observation2: 又不难知道加减运算过程中结果仍是 gcd\text{gcd}gcd 的倍数。所以去掉不影响答案。
因此我们可以将 x,yx,yx,y 都除以他们的 gcd\text{gcd}gcd。那么此时得到的 x,yx,yx,y 均为奇数且互质。
接下来假设 x>yx>yx>y,我们可以同时除以他们的 gcd\text{gcd}gcd。
由于 x,yx,yx,y 同为奇,故 y∗2,x−yy*2,x-yy∗2,x−y 都是偶数。
再假设 y∗2,x−yy*2,x-yy∗2,x−y 有与 222 互质的公因数 k(k≠1)k(k\ne 1)k(k=1)。
则 k∣y∧k∣x−yk\mid y\wedge k\mid x-yk∣y∧k∣x−y,所以 k∣xk\mid xk∣x,这与 (x,y)=1(x,y)=1(x,y)=1 矛盾。
所以这个 gcd\text{gcd}gcd 一定为 222。
所以 f(x,y)f(x,y)f(x,y) 等价于 f(x−y2,y)f(\frac{x-y}{2},y)f(2x−y,y)。
故其和每次都会除以 222,直到 x=y=1x=y=1x=y=1 或死循环。
这样我们就可以求出 f(i,j)f(i,j)f(i,j) 的值为 g(i+jgcd(i,j))g(\frac{i+j}{\gcd(i,j)})g(gcd(i,j)i+j),其中当 x=2kx=2^kx=2k 时,g(x)=kg(x)=kg(x)=k;否则 g(k)=0g(k)=0g(k)=0。
假设 x,yx,yx,y 最简比为 a,ba,ba,b,即 xa=yb\frac xa=\frac ybax=by。
我们不妨枚举 2i2^i2i,使得 a+b=2ia+b=2^ia+b=2i,然后枚举 aaa,自然就知道了 bbb。还需要检查一下 a,ba,ba,b 互质。
再枚举这个比例,进而也能知道 x,yx,yx,y。
显然这样的 (a,b)(a,b)(a,b) 有 2i2^i2i 种,且 xa=yb=d≤n2i−1\frac xa=\frac yb=d\le \frac{n}{2^{i-1}}ax=by=d≤2i−1n。
这样合法的不同的 (x,y)(x,y)(x,y) 只有 2n2n2n 对左右。
一共也就 O(nlogn)O(n\log n)O(nlogn) 的级别,我们完全可以将这些二元组记下来,问题就转换成了二维偏序问题。
就可以用树状数组做了。
时间复杂度 O(nlog2n)O(n\log^2n)O(nlog2n)。
其实由二离和分块的做法,但我不会哈哈哈
code
#include <bits/stdc++.h>
using namespace std;
#define maxn 200005
#define int long long
int n, m;
int a[maxn], p[maxn], ret[maxn], t[maxn];
vector < int > G[maxn];
vector < pair < int, int > > Q[maxn];int gcd( int x, int y ) {if( ! y ) return x;else return gcd( y, x % y );
}void add( int i, int x ) {for( ;i <= n;i += i & -i ) t[i] += x;
}int ask( int i ) {int ans = 0;\for( ;i;i -= i & -i ) ans += t[i];return ans;
}int F( int x, int y ) {if( x == y ) return 1;if( x < y ) swap( x, y );return F( y << 1, x - y ) + 1;
}void solve() {for( int i = 1;i <= n;i ++ ) {for( int j : G[i] ) {int k = F( a[j], a[i] );add( 1, k ), add( j + 1, -k );}for( auto j : Q[i] ) ret[j.second] = ask( j.first );}
}signed main() {scanf( "%lld", &n );for( int i = 1;i <= n;i ++ ) scanf( "%lld", &a[i] ), p[a[i]] = i;for( int i = 0;(1 << i) <= (n << 1);i ++ ) {for( int j = 1;j <= n;j ++ ) {int k = (1 << i) - a[j];if( k <= 0 or k > n or p[k] > j or a[j] == k ) continue;if( gcd( k, a[j] ) ^ 1 ) continue;for( int o = 1;p[o * k] and p[o * a[j]];o ++ ) {int x = p[o * k], y = p[o * a[j]];if( x > y ) swap( x, y );G[y].push_back( x );}}}scanf( "%lld", &m );for( int i = 1, l, r;i <= m;i ++ ) {scanf( "%lld %lld", &l, &r );Q[r].push_back( make_pair( l, i ) );}solve();for( int i = 1;i <= m;i ++ ) printf( "%lld\n", ret[i] );return 0;
}