「SDOI2014」数表

「SDOI2014」数表

σ(x) σ ( x ) x x 的约束和,求

i=1nj=1mk=1ak[σ(gcd(i,j))=k]

数据组数 <=20000 <= 20000
n,m<=100000,a<=109 n , m <= 100000 , a <= 10 9


很明显 σ(gcd(i,j))=k σ ( g c d ( i , j ) ) = k 并不好求,所以我们将他拆开

ans=k=1limσ(k)(i=1nj=1m[gcd(i,j)=k]) a n s = ∑ k = 1 l i m σ ( k ) ( ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = k ] )

很明显后面括号内的东西我们可以反演

f(k)=i=1nj=1m[gcd(i,j)=k] f ( k ) = ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = k ]

g(k)=i=1nj=1m[gcd(i,j)%k=0]=nkmk g ( k ) = ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) % k = 0 ] = ⌊ n k ⌋ ⌊ m k ⌋

g(x)=x|df(d) ∴ g ( x ) = ∑ x | d f ( d )

f(x)=x|dμ(dx)g(d) ⇒ f ( x ) = ∑ x | d μ ( d x ) g ( d )

f(x)=x|dμ(dx)ndmd f ( x ) = ∑ x | d μ ( d x ) ⌊ n d ⌋ ⌊ m d ⌋

带入原式中

ans=k=1limσ(k)(k|dμ(dk)ndmd) a n s = ∑ k = 1 l i m σ ( k ) ( ∑ k | d μ ( d k ) ⌊ n d ⌋ ⌊ m d ⌋ )

交换一下

ans=k=1limk|dσ(k)μ(dk)ndmd a n s = ∑ k = 1 l i m ∑ k | d σ ( k ) μ ( d k ) ⌊ n d ⌋ ⌊ m d ⌋

改为枚举 d d d的约数 k k

ans=d=1limk|dσ(k)μ(dk)ndmd

交换一下

ans=d=1limndmd(k|dσ(k)μ(dk)) a n s = ∑ d = 1 l i m ⌊ n d ⌋ ⌊ m d ⌋ ( ∑ k | d σ ( k ) μ ( d k ) )

我们可以线性筛求出 σ(x),μ(x) σ ( x ) , μ ( x )
我们每次要求的是 σ(x)<=a,x<=max{gcd(n,m)} σ ( x ) <= a , x <= m a x { g c d ( n , m ) } ans a n s
将询问按 a a 从小到大排序,σ(x)按值从小到大排序,每次询问完处理 alast<σ(x)<=athis a l a s t < σ ( x ) <= a t h i s σ(x) σ ( x )

可以遇见的是,我们最多要处理 n n σ(x),每次查询的复杂度是 O(nlogn) O ( n l o g n )

所以总复杂度为 O(nlog2n+qnlogn) O ( n l o g 2 n + q n l o g n )
(并不知道为什么处理 σ(x) σ ( x ) 的复杂度是 O(nlog2n) O ( n l o g 2 n ) ,我觉得应该是 O(nnlogn) O ( n n l o g n ) 呀。。求大神解答)

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值