hdu 4676 分块

给定一个1-N的全排列序列,N<=20000,有Q组询问,Q<=20000,每组询问给出左右区间[l, r],calculate sum of gcd(a[i], a[j]) for every L <= i < j <= R.


已知数 A 的因子 d 在区间其他数中出现了 k 次,那么如果 d 是所有gcd(A, other)的值时,那么最终的结果加上k*d,但这是不一定的,且 d 的因子肯定也是 A 的因子,当枚举到 d 的因子 d' 时显然就不能够加了。

设每个因子一个容斥因子f(d)

使得 k*f(d) + k*f(d') + k*f(d'') + ... + k*f(1) = k*d,

这样如果 d 出现 k 次,那么其因子的因子等等就也会出现 k 次,而将他们都统计起来的最终结果就是等效于 k*d。可以证明f(d) = phi(d),后者为欧拉函数。

n = sum{phi(d) , d|n}


typedef long long LL ;

const int maxn = 20000 ;
vector <int> factor[maxn + 8] ;
LL  phi[maxn + 8] ;

struct  Q{
        int l , r , id , p ;
        friend bool operator < (const Q A , const Q B){
             if(A.p == B.p) return A.r < B.r ;
             else  return A.p < B.p ;
        }
}q[maxn + 8] ;

LL   a[maxn + 8] ;
LL   cnt[maxn + 8] ;
LL   ans[maxn + 8] ;
int  L , R  ; LL  sum ;

LL   add(int x){
     LL s = 0 ; int c  ;
     for(int i = 0 ; i < factor[x].size() ; i++){
          c = factor[x][i] ;
          s += cnt[c] * phi[c] ;
          cnt[c]++ ;
     }
     return s ;
}

LL   del(int x){
     LL s = 0 ; int c  ;
     for(int i = 0 ; i < factor[x].size() ; i++){
          c = factor[x][i] ;
          cnt[c]-- ;
          s += cnt[c] * phi[c] ;
     }
     return s ;
}


LL   ask(int l , int r , int id){
     int i ;
     if(id == 0){
         sum = 0 ;
         for(i = l ; i <= r ; i++)  sum += add(a[i]) ;
         L = l , R = r ;
         return sum ;
     }
     for(i = l ; i < L ; i++) sum += add(a[i]) ;
     for(i = L ; i < l ; i++) sum -= del(a[i]) ;
     for(i = R+1 ; i <= r ; i++) sum += add(a[i]) ;
     for(i = r+1 ; i<= R ; i++) sum -= del(a[i]) ;
     L = l , R = r ;
     return sum ;
}

int  main(){
     int i , j ;
     for(i = 1 ; i <= maxn ; i++){
         for(j = i ; j <= maxn ; j += i)
             factor[j].push_back(i) ;
     }
     for(i = 1 ; i <= maxn ; i++) phi[i] = i ;
     for(i = 2 ; i <= maxn ; i++){
          if(phi[i] == i){
              for(j = i ; j <= maxn ; j += i)
                  phi[j] = phi[j]/i*(i-1) ;
          }
     }

     int n  , m  , t  , T = 1 ;
     int blocksize ;
     cin>>t ;
     while(t--){
          scanf("%d" , &n) ;
          for(i = 1 ; i <= n ; i++) scanf("%I64d" ,&a[i]) ;
          blocksize = sqrt(0.5 + n) ;
          scanf("%d" , &m) ;
          for(i = 0 ; i < m ; i++){
               scanf("%d%d" , &q[i].l , &q[i].r) ;
               q[i].id = i ;
               q[i].p = q[i].l / blocksize ;
          }
          memset(cnt , 0 , sizeof(cnt)) ;
          sort(q , q+m) ;
          for(i = 0 ; i < m ; i++)
              ans[q[i].id] = ask(q[i].l , q[i].r , i) ;
          printf("Case #%d:\n" , T++) ;
          for(i = 0 ; i < m ; i++) printf("%I64d\n" , ans[i]) ;
     }
     return 0 ;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值