1. 程式人生 > >HDU 4407 Sum(容斥原理+質因數分解)

HDU 4407 Sum(容斥原理+質因數分解)

題意:

給一個長度為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; }