「SDOI2014」数表
设σ(x)σ(x)为xx的约束和,求
数据组数<=20000<=20000
n,m<=100000,a<=109n,m<=100000,a<=109
很明显σ(gcd(i,j))=kσ(gcd(i,j))=k并不好求,所以我们将他拆开
ans=∑k=1limσ(k)(∑i=1n∑j=1m[gcd(i,j)=k])ans=∑k=1limσ(k)(∑i=1n∑j=1m[gcd(i,j)=k])
很明显后面括号内的东西我们可以反演
设
f(k)=∑i=1n∑j=1m[gcd(i,j)=k]f(k)=∑i=1n∑j=1m[gcd(i,j)=k]
g(k)=∑i=1n∑j=1m[gcd(i,j)%k=0]=⌊nk⌋⌊mk⌋g(k)=∑i=1n∑j=1m[gcd(i,j)%k=0]=⌊nk⌋⌊mk⌋
∴g(x)=∑x|df(d)∴g(x)=∑x|df(d)
⇒f(x)=∑x|dμ(dx)g(d)⇒f(x)=∑x|dμ(dx)g(d)
f(x)=∑x|dμ(dx)⌊nd⌋⌊md⌋f(x)=∑x|dμ(dx)⌊nd⌋⌊md⌋
带入原式中
ans=∑k=1limσ(k)(∑k|dμ(dk)⌊nd⌋⌊md⌋)ans=∑k=1limσ(k)(∑k|dμ(dk)⌊nd⌋⌊md⌋)
交换一下
ans=∑k=1lim∑k|dσ(k)μ(dk)⌊nd⌋⌊md⌋ans=∑k=1lim∑k|dσ(k)μ(dk)⌊nd⌋⌊md⌋
改为枚举dd与的约数kk
交换一下
ans=∑d=1lim⌊nd⌋⌊md⌋(∑k|dσ(k)μ(dk))ans=∑d=1lim⌊nd⌋⌊md⌋(∑k|dσ(k)μ(dk))
我们可以线性筛求出σ(x),μ(x)σ(x),μ(x)
我们每次要求的是σ(x)<=a,x<=max{gcd(n,m)}σ(x)<=a,x<=max{gcd(n,m)}的ansans
将询问按aa从小到大排序,按值从小到大排序,每次询问完处理alast<σ(x)<=athisalast<σ(x)<=athis 的σ(x)σ(x)。
可以遇见的是,我们最多要处理nn个,每次查询的复杂度是O(n‾√logn)O(nlogn)。
所以总复杂度为O(nlog2n+qn‾√logn)O(nlog2n+qnlogn)
(并不知道为什么处理σ(x)σ(x)的复杂度是O(nlog2n)O(nlog2n),我觉得应该是O(nn‾√logn)O(nnlogn)呀。。求大神解答)
code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 200000;
const int M = 120000;
bool is_prime[N];
int prime[N], tot = 0, mobius[N], max_n = 0;
ll num[N], qsum[N], dsum[N];
inline ll ksm( ll a, int b )
{
ll ret = 1;
while( b )
{
if( b & 1 ) ret *= a;
b >>= 1; a *= a;
}
return ret;
}
void init( int size )
{
memset( is_prime, true, sizeof( is_prime ) );
is_prime[1] = false; mobius[1] = 1;
dsum[1] = num[1] = qsum[1] = 1;
for( int i = 2; i <= size; i ++ )
{
if( is_prime[i] )
{
prime[++tot] = i;
num[i] = 1; mobius[i] = -1;
dsum[i] = qsum[i] = i+1;
}
for( int j = 1; j <= tot; j ++ )
{
int k = i * prime[j];
if( k > size ) break;
is_prime[k] = false;
if( i % prime[j] == 0 )
{
mobius[k] = 0;
num[k] = num[i] + 1;
qsum[k] = qsum[i] + ksm( prime[j], num[i] + 1 );
dsum[k] = dsum[i] / qsum[i] * qsum[k];
break;
}
mobius[k] = - mobius[i];
num[k] = 1; qsum[k] = prime[j] + 1;
dsum[k] = dsum[i] * dsum[prime[j]];
}
}
}
struct tQuery {
int n, m, a, q_id; ll ans;
// bool operator < ( const tQuery &rhs ) const { return a < rhs.a; }
} q[N];
struct tDsum {
int num; ll sum;
inline void init( int _num, ll _sum ) { num = _num; sum = _sum; }
bool operator < ( const tDsum &rhs ) const
{
// if( num > max_n ) return false;
// if( rhs.num > max_n ) return true;
return sum < rhs.sum;
}
} d[N];
bool cmp_a( tQuery x, tQuery y ) { return x.a < y.a; }
bool cmp_id( tQuery a, tQuery b ) { return a.q_id < b.q_id; }
int now_p = 1;
int t[N];
void add(int x,int val)
{
for(int i=x;i<=max_n;i+=i&-i)t[i]+=val;
}
int query(int x)
{
int tmp=0;
for(int i=x;i;i-=i&-i)tmp+=t[i];
return tmp;
}
inline void build_seg( int ed )
{
while( d[now_p].sum <= ed && now_p < M )
{
if( d[now_p].num > max_n ) { now_p ++; continue; }
// printf( "now_p.sum:%d\n", d[now_p].sum );
for( int i = d[now_p].num; i <= max_n; i += d[now_p].num )
{
int tmp = mobius[i/d[now_p].num];
add( i, d[now_p].sum * tmp );
// seg.modify( 1, 1, M, i, d[now_p].sum * tmp );
// printf( "[ADD] j:%d, val:%d\n", i, d[now_p].sum * tmp );
}
now_p ++;
}
}
int main()
{
// freopen( "g8.in", "r", stdin ); /
// freopen( "g8.out", "w", stdout );
int ttt; scanf( "%d", &ttt );
for( int i = 1; i <= ttt; i ++ )
{
scanf( "%d%d%d", &q[i].n, &q[i].m, &q[i].a );
q[i].q_id = i; if( q[i].n > q[i].m ) swap( q[i].n, q[i].m );
max_n = max( max_n, q[i].m );
}
init( M );
// for( int i = 1;i <= 20; i ++ )
// printf( "%lld ", dsum[i] ); printf( "dsum\n" );
for( int i = 1; i <= M; i ++ )
d[i].init( i, dsum[i] );
sort( d+1, d+M+1 );
// printf( "ok" );
sort( q+1, q+ttt+1, cmp_a );
// printf( "ok2" );
for( int i = 1; i <= ttt; i ++ )
{
// printf( "i:%d\n", i );
build_seg( q[i].a );
int lim = min( q[i].n, q[i].m );
int n = q[i].n, m = q[i].m, next = 0;
for( int j = 1; j <= lim; j = next + 1 )
{
// printf( "j:%d\n", j );
next = min( n/(n/j), m/(m/j) ); if( next > lim ) next = lim;
q[i].ans += 1ll * (n/j) * (m/j) * ( query( next ) - query( j-1 ) );
// printf( "n/j:%d, m/j:%d, seg:%d, ans:%lld\n", n/j, m/j, seg.query( 1, 1, M, j, next ), q[i].ans );
}
}
sort( q+1, q+ttt+1, cmp_id );
for( int i = 1; i <= ttt; i ++ )
printf( "%lld\n", q[i].ans % (1ll<<31) );
return 0;
}