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;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值