HDU 4676 Sum Of Gcd(欧拉函数求区间gcd之和+分块算法)

HDU 4676 Sum Of Gcd

题意:

给一个数列a,以及一些询问[L,R] ,求每一个询问的

Li<jRgcd(i,j)

思路:

S(L,R)=Li<jRgcd(i,j)

例如,当 a[L:R]={4,5,8} 时, S(L,R)=1+1+4=6

由于最大公约数本身定义,我们可以猜想多个数的gcd之和一定这些数约数个数有关。

比如说上例,其中4的约数有1,2,4;5的约数有1,5;8的约数有1,2,4,8.
num(d)= 约数d的出现次数。
那么

num(1)=3,num(2)=2,num(4)=2,num(5)=1,num(8)=1

猜想结论为:
S(L,R)=dC2num(d)

这里的d表示所有 a[L:R] 的所有约数。
但经过上例验算,问题出在重复计数,比如4与8的公约数有1,2,4,但我们要的是最大公约数,上式把每一个约数都统计了。
所以要改为容斥计数:
S(L,R)=f(d)C2num(d),d|nf(d)=n

后面这个限制条件由来:对于一个统计过的数n,那么他的约数一定被统计过,为了避免重复统计,利用莫比乌斯的性质,对于n的所有约数的统计次数之和应等于n,f其实就是欧拉函数,于是
f(d)=φ(d), S(L,R)=φ(d)C2num(d)

由于直接暴力搞会TLE,于是用分块操作把查询离线处理:

往区间添加一个值n的时候,

d|nφ(d)C2num(d) 变为 d|nφ(d)C2num(d)+1 ,

实际上相当于增加 d|nφ(d)C1num(d)=d|nφ(d)num(d)

同理往区间删去一个值n的时候,相当于减少 d|nφ(d)num(d)

时间复杂度: O(mn+nn)

代码:

/*
* @author FreeWifi_novicer
* language : C++/C
*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<string>
#include<map>
#include<set>
#include<vector>
#include<queue>

using namespace std;

#define clr( x , y ) memset(x,y,sizeof(x))
#define cls( x ) memset(x,0,sizeof(x))
#define pr( x ) cout << #x << " = " << x << endl 
#define pri( x ) cout << #x << " = " << x << " " 
#define test( t ) int t ; cin >> t ; int kase = 1 ; while( t-- )
#define out( kase ) printf( "Case #%d:\n" , kase++ )
#define mp make_pair
#define pii pair<int,int>
#define pb push_back
typedef long long lint;
typedef long long ll;
typedef long long LL;


const int maxn = 2 * 1e4 + 5 ;
int L , R , phi[maxn+5] , a[maxn+5] , num[maxn+5] ;
vector<int> d[maxn+5] ;
lint sum , ans[maxn+5];

struct Node{
    int l , r , id , s ;
    bool operator < ( const Node &a )const{
        if( s != a.s ) return s < a.s ;
        return r < a.r ;
    }
}q[maxn+5] ;

void getPhi()
{
    cls( phi ) ;
    phi[1] = 1 ;
    for( int i = 2 ; i < maxn ; i++ )
    {
        if( !phi[i] )
            for( int j = i ; j < maxn ; j += i )
            {
                if( !phi[j] ) phi[j] = j ;
                phi[j] = phi[j] / i * ( i - 1 ) ;
            }
    }
}

void getFac()
{
    for( int i = 1 ; i < maxn ; i++ )
        for( int j = i ; j < maxn ; j += i )
            d[j].pb( i ) ;
}
void init()
{
    getFac() ; getPhi() ;
}
void insert( int x )
{
    for( int i = 0 ; i < (int)d[x].size() ; i++ )
        sum += phi[d[x][i]] * ( num[d[x][i]]++ ) ;
}
void del( int x )
{
    for( int i = 0 ; i < (int)d[x].size() ; i++ )
        sum -= phi[d[x][i]] * ( --num[d[x][i]] ) ;
}
lint query( int l , int r , int x )
{
    if( !x )
    {
        sum = 0 ; 
        for( int i = l ; i <= r ; i++ ) 
            insert( a[i] ) ;
    }
    else
    {
        for( int i = l ; i < L ; i++ ) insert( a[i] ) ;
        for( int i = R + 1 ; i <= r ; i++ ) insert( a[i] ) ;
        for( int i = L ; i < l ; i++ )  del( a[i] ) ;
        for( int i = r + 1 ; i <= R ; i++ )  del( a[i] ) ;
    }
    L = l ; R = r ;
    return sum ;
}
int main(){
    //freopen("input.txt","r",stdin);
    init() ;
    test( t )
    {
        cls( num ) ;
        int n ; cin >> n ;
        int magic = (int)sqrt( n + 0.5 ) ;
        for( int i = 1 ; i <= n ; i++ )
            scanf( "%d" , a + i ) ;
        int m ; cin >> m ;
        for( int i = 0 ; i < m ; i++ )
        {
            scanf( "%d%d" , &q[i].l , &q[i].r ) ;
            q[i].id = i ; q[i].s = q[i].l / magic ;
        }
        sort( q , q + m ) ;
        for( int i = 0 ; i < m ; i++ )
        {
            ans[q[i].id] = query( q[i].l , q[i].r , i ) ;
        }
        out( kase ) ;
        for( int i = 0 ; i < m ; i++ )
        {
            printf( "%I64d\n" , ans[i] ) ;
        }
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值