ACM-ICPC 2018 沈阳赛区网络预赛 G Spare Tire(容斥)

https://nanti.jisuanke.com/t/31448

题意

 已知a序列,给你一个n和m求小于n与m互质的数作为a序列的下标的和

分析

 打表发现ai=i*(i+1)。

易得前n项和为 Sn=n*(n+1)(2*n+1)/6+n*(n+1)/2;我们直接求与m互质的数较难,所以我们可以换个思路,求与 m不互质的数,那么与m不互质的数,是取m的素因子的乘积(因为根据唯一分解定理,任意个数都可看作的素数积),那么我们将m分解质因数,通过容斥定理,就可以得道与m不互质的数,总和sum减去这些数对应的a的和就是答案了。

容斥原理的具体如下:

          区间中与i不互质的个数 = (区间中i的每个质因数的倍数个数)-(区间中i的每两个质因数乘积的倍数)+(区间中i的每3个质因数的成绩的倍数个数)-(区间中i的每4个质因数的乘积)+...

         比如存在一个素因子是k,那么需要求下标为k,2k,3k,4k……的a的和,即求(kn)^2+kn通项的求和,为k^2*n*(n+1)*(2n+1)/6+k*n*(n+1)/2,项数为n/k。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn  = 1e4 + 10;
const int mod = 1e9 + 7;
ll inv6=166666668;
ll inv2=500000004;
ll a[maxn];
ll cal(ll n,ll x){
    n/=x;
    return (n*(n+1)%mod*(2*n+1)%mod*inv6%mod*x%mod*x%mod+n*(n+1)%mod*inv2%mod*x%mod)%mod;
}
int main(){
    ll n,m;
    while(~scanf("%lld%lld",&n,&m)){
        ll tot = cal(n,1);
        int cnt=0;
        for(ll i=2;i*i<=m;i++){
            if(m%i==0){
                a[cnt++]=i;
                while(m%i==0) m/=i;
            }
        }
        if(m!=1) a[cnt++]=m;
        ll res=0;
        for(int i=1;i<(1<<cnt);i++){
            ll tmp=1;
            for(int j=0;j<cnt;j++){
                if((1<<j)&i){
                    tmp=tmp*a[j]%mod;
                }
            }
            tmp=cal(n,tmp);
            if(__builtin_popcount(i)&1) res=(res+tmp)%mod;
            else res=(res-tmp+mod)%mod;
        }
        printf("%lld\n",(tot-res+mod)%mod);
    }
    return 0;
}

 

posted @ 2018-09-19 10:50  litos  阅读(132)  评论(0编辑  收藏  举报