HDU 4676 Sum Of Gcd(欧拉函数求区间gcd之和+分块算法)
2015-10-04 23:08
295 查看
HDU 4676 Sum Of Gcd
题意:
给一个数列a,以及一些询问[L,R] ,求每一个询问的 ∑L≤i<j≤Rgcd(i,j)思路:
令S(L,R)=∑L≤i<j≤Rgcd(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)=∑d∗C2num(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; }