1. 程式人生 > >2018ACM/ICPC瀋陽網路賽G Spare Tire

2018ACM/ICPC瀋陽網路賽G Spare Tire

題目連結

題意:定義數列an為,給定n、m,求a1到an中所有下標與m互質的數的和。

首先,通過打表或者遞推可以得到an的通項為n*(n+1)。然後,如果是求1到n中與m互質的數的個數,我們可以較容易的通過容斥原理得到與m不互質的數的個數,然後減去即可。那麼,如何求出題目要求的呢?

解題前須知:

1^{2}+2^{2}+...+n^{2}=\frac{n*(n+1)*(2*n+1)}{6}1+2+...+n=\frac{n*(n+1)}{2}

i*(i+1)=i^2+i,所以有 

       1^{2}+1+2^{2}+2+...+n^{2}+n=\frac{n*(n+1)*(2*n+1)}{6}+\frac{n*(n+1)}{2} =\frac{n*(n+1)*(n+2)}{3}

先給一道題的連結:hdu 4407 Sum。這道題要求1到n中與m互質的數的和,雖然資料範圍小一點,而且還有修改操作,但是可以發現要求的和與這道題是類似的。

假設對於無修改操作的hdu4407:將m分解質因數,然後列舉m的所有不含有重複質因子的因數,計算出每種因子的倍數的和,通過容斥計算出所有和m不互質的數的和,最後用n*(n+1)/2減去該值即可。可知對於每一個因子p,它的倍數的和為p*\frac{\left \lfloor \frac{n}{p} \right \rfloor*(\left \lfloor \frac{n}{p} \right \rfloor+1)}{2}

,用等差數列求和公式即可O(1)求出解,因此總的複雜度是容斥帶來的O(2^t)(假設t個質因子)。

而對於本題,不過是將與m互質的i的和變為了與m互質的i的i*(i+1)=i^2+i的和,那麼同樣計算出每種因子的倍數的i*(i+1)的和,通過容斥計算出所有與m不互質的數的i*(i+1)的和,最後用\frac{n*(n+1)*(n+2)}{3}減去該值即可。可知對於每一個因子p,它的倍數的i*(i+1)的和不方便直接通過公式計算,但是拆成i^2和i的和分別計算很方便,分別為p^{2}*\frac{\left \lfloor \frac{n}{p} \right \rfloor*(\left \lfloor \frac{n}{p} \right \rfloor+1)*(2*\left \lfloor \frac{n}{p} \right \rfloor+1)}{6}p*\frac{\left \lfloor \frac{n}{p} \right \rfloor*(\left \lfloor \frac{n}{p} \right \rfloor+1)}{2}

程式碼如下:

#include<bits/stdc++.h>
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
#define ll long long
const int mod=1e9+7;
map<int , int>v ;
vector<int>fac ;
int qpow(int a,int b){
	int res=1;
	while(b){
		if(b&1) res=res*1ll*a%mod;
		b>>=1;
		a=a*1ll*a%mod;
	}
	return res;
}
int inv;
ll cal( int l , int r , int val ){
    int tt=r/val;
    ll a1=(l%val==0)?l:(val-l%val)+l;
    ll an=r-r%val;
    ll res=((ll)(a1+an)*(ll)tt/2)%mod; // 等比數列求和公式
    res=(res+val*1ll*val%mod*tt%mod*(tt+1)%mod*(2*tt+1)%mod*inv%mod)%mod;//求倍數的i^2的和
    return res ;
}

ll work( int l , int r , int p ){
    fac.clear();
    for( int i = 2 ; i * i <= p ; i++ ){//暴力對m分解質因數
        if( p % i == 0 ){
            fac.pb( i ) ;
            while( p % i == 0 ) p /= i ;
        }
    }
    if( p > 1 ) fac.pb( p ) ;
    int s = fac.size() ;
    ll res = 0 ;
    for( int i = 1 ; i < ( 1 << s ) ; i++ ){//列舉因子
        int bits = 0 ;
        ll val = 1 ;
        for( ll j = 0 ; j < s ; j++ ){
            if( i & ( 1 << j ) ){
                bits++ ;
                val *= fac[j] ;
            }
        }
        ll tmp = cal( l , r , val ) ;
        if( bits & 1 ) // 容斥原理
            res += tmp ;
        else
            res -= tmp ;
    }
    ll sum=(ll)(l+r)*(ll)(r-l+1)/2%mod;
    sum=(sum+r*1ll*(r+1)%mod*(2*r+1)%mod*inv%mod)%mod;//sum為總和,減去不互質的和
    res=(sum-res)%mod;
    return res ;
}
ll solve( int l , int r , int p ){
    ll res = work( l , r , p ) ;
    return res ;
}
int n,m;
int main(){
  	inv=qpow(6,mod-2);//處理出6的逆元
  	while(~scanf("%d %d",&n,&m)){
  		ll  ans=solve(1,n,m);
  		printf("%lld\n",(ans%mod+mod)%mod);
    }
    return 0;
}