2018ACM/ICPC瀋陽網路賽G Spare Tire
阿新 • • 發佈:2019-02-09
題意:定義數列an為,給定n、m,求a1到an中所有下標與m互質的數的和。
首先,通過打表或者遞推可以得到an的通項為n*(n+1)。然後,如果是求1到n中與m互質的數的個數,我們可以較容易的通過容斥原理得到與m不互質的數的個數,然後減去即可。那麼,如何求出題目要求的呢?
解題前須知:
,
i*(i+1)=i^2+i,所以有
先給一道題的連結:hdu 4407 Sum。這道題要求1到n中與m互質的數的和,雖然資料範圍小一點,而且還有修改操作,但是可以發現要求的和與這道題是類似的。
假設對於無修改操作的hdu4407:將m分解質因數,然後列舉m的所有不含有重複質因子的因數,計算出每種因子的倍數的和,通過容斥計算出所有和m不互質的數的和,最後用n*(n+1)/2減去該值即可。可知對於每一個因子p,它的倍數的和為
而對於本題,不過是將與m互質的i的和變為了與m互質的i的i*(i+1)=i^2+i的和,那麼同樣計算出每種因子的倍數的i*(i+1)的和,通過容斥計算出所有與m不互質的數的i*(i+1)的和,最後用減去該值即可。可知對於每一個因子p,它的倍數的i*(i+1)的和不方便直接通過公式計算,但是拆成i^2和i的和分別計算很方便,分別為和。
程式碼如下:
#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; }