Sum(莫比乌斯反演 1~n与p互质数之和)

原题: http://acm.hdu.edu.cn/showproblem.php?pid=4407

题意:

原来的n长数组123…,有两种操作,一种是单点修改,一种是查询区间与p互质的数之和。

解析:

因为操作只有1000个,所以对于修改的数可以暴力做,对于原始的位置相当于做 [ 1 , R ] [1,R] [1,R]中与p互质的数之和。

这个用莫比乌斯做,设 f ( d ) = ∑ i < = R i ∗ [ g c d ( i , p ) = d ] f(d)=\sum_{i<=R}i*[gcd(i,p)=d] f(d)=i<=Ri[gcd(i,p)=d] g ( d ) = ∑ i < = R i ∗ [ d ∣ g c d ( i , p ) ] g(d)=\sum_{i<=R}i*[d|gcd(i,p)] g(d)=i<=Ri[dgcd(i,p)],有 g ( d ) = d + 2 d + . . . k d ( k d + d > R ) g(d)=d+2d+...kd(kd+d>R) g(d)=d+2d+...kd(kd+d>R)

g ( d ) g(d) g(d)表示的是 g c d ( i , p ) = k d gcd(i,p)=kd gcd(i,p)=kd,而 f ( d ) f(d) f(d)表示的是 g c d ( i , p ) = d gcd(i,p)=d gcd(i,p)=d

注意,此时的d的取值必须是p的因子,这个条件很关键,降低了时间复杂度


因为有 g ( d ) = ∑ f ( i ∗ d ) g(d)=\sum f(i*d) g(d)=f(id),所以 f ( d ) = ∑ u ( i ) ∗ g ( i ∗ d ) f(d)=\sum u(i)*g(i*d) f(d)=u(i)g(id)

答案为 f ( 1 ) = ∑ i u ( i ) ∗ g ( i ) f(1)=\sum_{i}u(i)*g(i) f(1)=iu(i)g(i)

这里的 i i i只需要枚举p的因子即可。

代码:

#include
using namespace std;
#define LL long long

const int maxn=4e5+5;
int mu[maxn],pri[maxn>>1],now;
bool vis[maxn];

void init(){
    mu[1]=1;
    for(int i=2;i<maxn;i++){
        if(!vis[i]){
            pri[++now]=i;
            mu[i]=-1;
        }
        for(int j=1;j<=now&&pri[j]*i<maxn;j++){
            vis[pri[j]*i]=1;
            if(i%pri[j])mu[i*pri[j]]=-mu[i];
            else{
                mu[i*pri[j]]=0;
                break;
            }
        }
    }
}
vector<int>fac[maxn];
LL deal(int r,int p){
    if(r<=0)return 0;
    if(fac[p].empty()){
        int en=sqrt(p);
        for(int i=1;i<=en;i++){
            if(p%i==0){
                fac[p].push_back(i);
                if(p/i!=i){
                    fac[p].push_back(p/i);
                }
            }
        }
    }
    LL ans=0;
    for(auto y:fac[p]){
        LL st=(LL)y;
        LL en=r/y*y;
        ans+=(LL)(mu[y])*(st+en)*(en/st)/2ll;
    }
    return ans;
}

int main(){
    init();

    int t;scanf("%d",&t);
    unordered_map<int,int>re;
    while(t--){
        int n,q;scanf("%d%d",&n,&q);
        re.clear();
        while(q--){
            int flag;scanf("%d",&flag);
            if(flag==1){
                int l,r,p;scanf("%d%d%d",&l,&r,&p);
                LL ans=deal(r,p)-deal(l-1,p);
                for(auto y:re){
                    if(y.first<=r&&y.first>=l){
                        if(__gcd(y.first,p)==1)ans-=(LL)y.first;
                        if(__gcd(y.second,p)==1)ans+=(LL)y.second;
                    }
                }
                printf("%lld\n",ans);
            }
            else{
                int a,b;scanf("%d%d",&a,&b);
                re[a]=b;
            }
        }
    }
}

你可能感兴趣的:(数论/数学,例题)