HDU 4407 Sum(容斥原理+質因數分解)
阿新 • • 發佈:2019-01-27
題意:
給一個長度為n的序列,序列由1~n依次組成。
對序列執行兩種操作:
1.查詢[x,y]內與p互素的數的和;
2.修改第x數為c.
思路:
往線段樹的方向想了半天,發現就是容斥原理略微變形,腦殘不可醫啊。。
修改操作可以用map進行對映。
查詢操作的話我們就把序列一直當做1~n的序列來查詢,然後迭代器跑一遍map判斷對查詢有無影響即可,總之操作最多2000次;
對於查詢操作,我們可以先分解出p的質因數,設p的每種質因數組合的裡的質因數乘積為value,那麼[x,y]內value的倍數與p必定不互素,求出value的區間[x,y]內所有倍數和(用等比數列求和公式),然後對結果進行容斥求和,即得出區間[x,y]內與p不互素的數的和sum,然後區間所有數的和(用等差數列求和公式)減去sum即可。
程式碼:
/*
* @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 mp make_pair
#define pb push_back
typedef long long lint;
typedef long long ll;
typedef long long LL;
map<int , int>v ;
vector<int>fac ;
int gcd( int x , int y ){
if( y == 0 ) return x ;
return gcd( y , x % y ) ;
}
lint cal( int l , int r , int val ){
int n = ( r / val ) - ( ( l - 1 ) / val ) ;
int a1 = ( l % val == 0 )? l : ( val - l % val ) + l ;
int an = r - r % val ;
lint res = (lint)( a1 + an ) * (lint)n / 2 ; // 等比數列求和公式
return res ;
}
lint work( int l , int r , int p ){
fac.clear() ;
for( int i = 2 ; i * i <= p ; i++ ){
if( p % i == 0 ){
fac.pb( i ) ;
while( p % i == 0 ) p /= i ;
}
}
if( p > 1 ) fac.pb( p ) ;
int s = fac.size() ;
lint res = 0 ;
for( int i = 1 ; i < ( 1 << s ) ; i++ ){
int bits = 0 ;
lint val = 1 ;
for( lint j = 0 ; j < s ; j++ ){
if( i & ( 1 << j ) ){
bits++ ;
val *= fac[j] ;
}
}
lint tmp = cal( l , r , val ) ;
if( bits & 1 ) // 容斥原理
res += tmp ;
else
res -= tmp ;
}
lint sum = (lint)( l + r ) * (lint)( r - l + 1 ) / 2 ;
res = sum - res ; // 等差數列求和公式
return res ;
}
lint solve( int l , int r , int p ){
lint res = work( l , r , p ) ;
if( v.empty() ) return res ;
map<int,int>::iterator it ;
for( it = v.begin() ; it != v.end() ; it++ ){
lint x = it->first , y = it->second ;
if( x > r || x < l ) continue ;
if( gcd( x , p ) == 1 ) res -= x ;
if( gcd( y , p ) == 1 ) res += y ;
}
return res ;
}
int main(){
//freopen("input.txt","r",stdin);
int t ; cin >> t ;
while( t-- ){
v.clear() ;
int n , m ;
cin >> n >> m ;
for( int i = 1 ; i <= m ; i++ ){
int op ;
scanf( "%d" , &op ) ;
if( op == 1 ){
int l , r , p ;
scanf( "%d%d%d" , &l , &r , &p ) ;
if( l > r ) swap( l , r ) ;
lint ans = solve( l , r , p ) ;
printf( "%I64d\n" , ans ) ;
}
else if( op == 2 ){
int pos , x ;
scanf( "%d%d" , &pos , &x ) ;
v[pos] = x ;
}
}
}
return 0;
}